diff --git a/all-model-chat/.github/workflows/quality-gates.yml b/all-model-chat/.github/workflows/quality-gates.yml new file mode 100644 index 00000000..1b4661a2 --- /dev/null +++ b/all-model-chat/.github/workflows/quality-gates.yml @@ -0,0 +1,29 @@ +name: Quality Gates + +on: + push: + branches: + - '**' + pull_request: + +permissions: + contents: read + +jobs: + quality-gates: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm install + + - name: Run quality checks + run: npm run ci:check diff --git a/all-model-chat/.gitignore b/all-model-chat/.gitignore index a547bf36..95c9df49 100644 --- a/all-model-chat/.gitignore +++ b/all-model-chat/.gitignore @@ -15,6 +15,9 @@ dist-ssr # Editor directories and files .vscode/* !.vscode/extensions.json +.github/* +!.github/workflows/ +!.github/workflows/** .idea .DS_Store *.suo @@ -22,3 +25,23 @@ dist-ssr *.njsproj *.sln *.sw? + +# Generated files +bundle-stats.html +stats.html +*.png +*.pdf +*.csv +*.pkl +__pycache__/ + +# Temporary folders +/chat-answer/ +/.agent/ +/chat/ +/setup/ + +# Environment files +.env +.env.local +.env.*.local diff --git a/all-model-chat/apps/bff/.env.example b/all-model-chat/apps/bff/.env.example new file mode 100644 index 00000000..9f96fa6d --- /dev/null +++ b/all-model-chat/apps/bff/.env.example @@ -0,0 +1,28 @@ +# BFF runtime configuration +BFF_HOST=127.0.0.1 +BFF_PORT=8787 +BFF_SERVICE_NAME=all-model-chat-bff +NODE_ENV=development + +# Provider key configuration +# Supports comma/newline separated key lists. +GEMINI_API_KEYS=your-first-key,your-second-key + +# Optional single key fallback +# GEMINI_API_KEY=your-single-key + +# Cooldown duration (milliseconds) after a key-level failure +BFF_KEY_FAILURE_COOLDOWN_MS=30000 + +# Provider endpoint mode +# false => Gemini API +# true => Vertex AI / Vertex Express +BFF_PROVIDER_VERTEXAI=false + +# Optional provider endpoint override (full base URL without trailing slash) +# Example for Vertex Express: https://aiplatform.googleapis.com +# BFF_PROVIDER_BASE_URL=https://aiplatform.googleapis.com + +# Optional API version override +# Example for Vertex Express stable: v1 +# BFF_PROVIDER_API_VERSION=v1 diff --git a/all-model-chat/apps/bff/package.json b/all-model-chat/apps/bff/package.json new file mode 100644 index 00000000..ab3729dc --- /dev/null +++ b/all-model-chat/apps/bff/package.json @@ -0,0 +1,18 @@ +{ + "name": "@all-model-chat/bff", + "private": true, + "version": "1.8.5", + "type": "module", + "main": "dist/index.js", + "scripts": { + "dev": "npm run build && npm run start", + "build": "tsc -p tsconfig.json", + "start": "node dist/index.js", + "lint": "tsc -p tsconfig.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", + "test": "node --test" + }, + "dependencies": { + "@google/genai": "^1.2.0" + } +} diff --git a/all-model-chat/apps/bff/src/config/env.ts b/all-model-chat/apps/bff/src/config/env.ts new file mode 100644 index 00000000..74226496 --- /dev/null +++ b/all-model-chat/apps/bff/src/config/env.ts @@ -0,0 +1,81 @@ +export interface BffConfig { + host: string; + port: number; + nodeEnv: string; + serviceName: string; + providerApiKeys: string[]; + providerKeyFailureCooldownMs: number; + providerUseVertexAi: boolean; + providerBaseUrl?: string; + providerApiVersion?: string; +} + +const parsePort = (rawPort: string | undefined, fallback: number): number => { + if (!rawPort) return fallback; + + const parsed = Number(rawPort); + if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) { + throw new Error(`Invalid BFF_PORT value: ${rawPort}`); + } + + return parsed; +}; + +const parsePositiveInteger = (rawValue: string | undefined, fallback: number, fieldName: string): number => { + if (!rawValue) return fallback; + + const parsed = Number(rawValue); + if (!Number.isInteger(parsed) || parsed <= 0) { + throw new Error(`Invalid ${fieldName} value: ${rawValue}`); + } + + return parsed; +}; + +const parseBoolean = (rawValue: string | undefined, fallback: boolean, fieldName: string): boolean => { + if (!rawValue) return fallback; + + const normalized = rawValue.trim().toLowerCase(); + if (normalized === 'true' || normalized === '1' || normalized === 'yes' || normalized === 'on') { + return true; + } + if (normalized === 'false' || normalized === '0' || normalized === 'no' || normalized === 'off') { + return false; + } + + throw new Error(`Invalid ${fieldName} value: ${rawValue}`); +}; + +const parseOptionalString = (rawValue: string | undefined): string | undefined => { + if (!rawValue) return undefined; + + const trimmed = rawValue.trim(); + return trimmed.length > 0 ? trimmed : undefined; +}; + +const parseProviderApiKeys = (rawList: string | undefined, rawSingle: string | undefined): string[] => { + const merged = [rawList || '', rawSingle || ''].join('\n'); + + return merged + .split(/[\n,]+/) + .map((key) => key.trim()) + .filter((key) => key.length > 0); +}; + +export const loadBffConfig = (): BffConfig => { + return { + host: process.env.BFF_HOST || '127.0.0.1', + port: parsePort(process.env.BFF_PORT, 8787), + nodeEnv: process.env.NODE_ENV || 'development', + serviceName: process.env.BFF_SERVICE_NAME || 'all-model-chat-bff', + providerApiKeys: parseProviderApiKeys(process.env.GEMINI_API_KEYS, process.env.GEMINI_API_KEY), + providerKeyFailureCooldownMs: parsePositiveInteger( + process.env.BFF_KEY_FAILURE_COOLDOWN_MS, + 30000, + 'BFF_KEY_FAILURE_COOLDOWN_MS' + ), + providerUseVertexAi: parseBoolean(process.env.BFF_PROVIDER_VERTEXAI, false, 'BFF_PROVIDER_VERTEXAI'), + providerBaseUrl: parseOptionalString(process.env.BFF_PROVIDER_BASE_URL), + providerApiVersion: parseOptionalString(process.env.BFF_PROVIDER_API_VERSION), + }; +}; diff --git a/all-model-chat/apps/bff/src/index.ts b/all-model-chat/apps/bff/src/index.ts new file mode 100644 index 00000000..2305aed6 --- /dev/null +++ b/all-model-chat/apps/bff/src/index.ts @@ -0,0 +1,119 @@ +import { createServer } from 'node:http'; +import { loadBffConfig } from './config/env.js'; +import { createHealthPayload } from './routes/health.js'; +import { ProviderKeyPool } from './providers/keyPool.js'; +import { GeminiProviderClient } from './providers/geminiClient.js'; +import { handleChatStreamRoute } from './routes/chatStream.js'; +import { handleFilesRoute } from './routes/files.js'; +import { handleGenerationRoute } from './routes/generation.js'; + +const config = loadBffConfig(); +const keyPool = new ProviderKeyPool(config.providerApiKeys, { + failureCooldownMs: config.providerKeyFailureCooldownMs, +}); +const geminiProviderClient = new GeminiProviderClient(keyPool, { + useVertexAi: config.providerUseVertexAi, + baseUrl: config.providerBaseUrl, + apiVersion: config.providerApiVersion, +}); + +const server = createServer((request, response) => { + if (!request.url) { + response.writeHead(400, { 'content-type': 'application/json; charset=utf-8' }); + response.end(JSON.stringify({ error: 'Invalid request URL' })); + return; + } + + const method = request.method || 'GET'; + const path = request.url.split('?')[0]; + + if (method === 'GET' && path === '/health') { + const payload = createHealthPayload( + config, + geminiProviderClient.getKeyPoolSnapshot(), + geminiProviderClient.getProviderConfigSnapshot() + ); + response.writeHead(200, { 'content-type': 'application/json; charset=utf-8' }); + response.end(JSON.stringify(payload)); + return; + } + + if (path === '/api/chat/stream') { + if (method !== 'POST') { + response.writeHead(405, { + 'content-type': 'application/json; charset=utf-8', + allow: 'POST', + }); + response.end(JSON.stringify({ error: 'Method Not Allowed' })); + return; + } + + handleChatStreamRoute(request, response, geminiProviderClient).catch((error) => { + if (response.writableEnded) { + return; + } + + const message = error instanceof Error ? error.message : 'Unexpected stream proxy failure.'; + response.writeHead(500, { 'content-type': 'application/json; charset=utf-8' }); + response.end( + JSON.stringify({ + error: { + code: 'internal_error', + message, + status: 500, + }, + }) + ); + }); + return; + } + + if (path.startsWith('/api/files/')) { + handleFilesRoute(request, response, geminiProviderClient).catch((error) => { + if (response.writableEnded) return; + const message = error instanceof Error ? error.message : 'Unexpected files route failure.'; + response.writeHead(500, { 'content-type': 'application/json; charset=utf-8' }); + response.end( + JSON.stringify({ + error: { + code: 'internal_error', + message, + status: 500, + }, + }) + ); + }); + return; + } + + if (path.startsWith('/api/generation/')) { + handleGenerationRoute(request, response, geminiProviderClient).catch((error) => { + if (response.writableEnded) return; + const message = error instanceof Error ? error.message : 'Unexpected generation route failure.'; + response.writeHead(500, { 'content-type': 'application/json; charset=utf-8' }); + response.end( + JSON.stringify({ + error: { + code: 'internal_error', + message, + status: 500, + }, + }) + ); + }); + return; + } + + response.writeHead(404, { 'content-type': 'application/json; charset=utf-8' }); + response.end(JSON.stringify({ error: 'Not Found' })); +}); + +server.listen(config.port, config.host, () => { + console.log(`[BFF] ${config.serviceName} listening on http://${config.host}:${config.port}`); + console.log(`[BFF] Provider key pool initialized with ${config.providerApiKeys.length} key(s).`); + console.log( + `[BFF] Provider mode: ${config.providerUseVertexAi ? 'vertexai' : 'gemini-api'} (baseUrl=${ + config.providerBaseUrl || 'default' + }, apiVersion=${config.providerApiVersion || 'default'})` + ); +}); diff --git a/all-model-chat/apps/bff/src/providers/geminiClient.ts b/all-model-chat/apps/bff/src/providers/geminiClient.ts new file mode 100644 index 00000000..3502254d --- /dev/null +++ b/all-model-chat/apps/bff/src/providers/geminiClient.ts @@ -0,0 +1,96 @@ +import { GoogleGenAI } from '@google/genai'; +import { ProviderKeyPool, KeyPoolSnapshot } from './keyPool.js'; + +export interface GeminiClientContext { + client: GoogleGenAI; + keyId: string; +} + +export interface GeminiProviderClientOptions { + useVertexAi: boolean; + baseUrl?: string; + apiVersion?: string; +} + +export class GeminiProviderClient { + constructor( + private readonly keyPool: ProviderKeyPool, + private readonly options: GeminiProviderClientOptions + ) {} + + async withClient(operation: (context: GeminiClientContext) => Promise): Promise { + const { apiKey, keyId } = this.keyPool.acquireKey(); + const clientOptions: Record = { + apiKey, + vertexai: this.options.useVertexAi, + }; + + if (this.options.apiVersion) { + clientOptions.apiVersion = this.options.apiVersion; + } + + if (this.options.baseUrl) { + clientOptions.httpOptions = { + baseUrl: this.options.baseUrl, + }; + } + + const client = new GoogleGenAI(clientOptions as any); + + try { + const result = await operation({ client, keyId }); + this.keyPool.reportSuccess(keyId); + return result; + } catch (error) { + if (this.shouldReportKeyFailure(error)) { + this.keyPool.reportFailure(keyId); + } + throw error; + } + } + + getKeyPoolSnapshot(): KeyPoolSnapshot { + return this.keyPool.getSnapshot(); + } + + getProviderConfigSnapshot(): { + useVertexAi: boolean; + baseUrl: string | null; + apiVersion: string | null; + } { + return { + useVertexAi: this.options.useVertexAi, + baseUrl: this.options.baseUrl || null, + apiVersion: this.options.apiVersion || null, + }; + } + + private shouldReportKeyFailure(error: unknown): boolean { + const message = error instanceof Error ? error.message : ''; + if (message.includes('does not support uploading files')) { + return false; + } + if (message.includes('only supported by the Gemini Developer API')) { + return false; + } + + const status = this.readNumericStatus(error); + if (status === 400 || status === 404) { + return false; + } + + return true; + } + + private readNumericStatus(error: unknown): number | null { + if (!error || typeof error !== 'object') return null; + + const status = (error as Record).status; + if (typeof status === 'number' && Number.isFinite(status)) return status; + + const statusCode = (error as Record).statusCode; + if (typeof statusCode === 'number' && Number.isFinite(statusCode)) return statusCode; + + return null; + } +} diff --git a/all-model-chat/apps/bff/src/providers/keyPool.ts b/all-model-chat/apps/bff/src/providers/keyPool.ts new file mode 100644 index 00000000..7f02797d --- /dev/null +++ b/all-model-chat/apps/bff/src/providers/keyPool.ts @@ -0,0 +1,106 @@ +export interface KeyPoolOptions { + failureCooldownMs: number; +} + +export interface AcquiredProviderKey { + apiKey: string; + keyId: string; +} + +interface ProviderKeyState { + apiKey: string; + keyId: string; + successCount: number; + failureCount: number; + cooldownUntilEpochMs: number; +} + +export interface KeyPoolSnapshotItem { + keyId: string; + successCount: number; + failureCount: number; + cooldownRemainingMs: number; +} + +export interface KeyPoolSnapshot { + configuredKeyCount: number; + availableKeyCount: number; + failureCooldownMs: number; + keys: KeyPoolSnapshotItem[]; +} + +export class ProviderKeyPool { + private readonly states: ProviderKeyState[]; + private readonly failureCooldownMs: number; + private nextIndex = 0; + + constructor(apiKeys: string[], options: KeyPoolOptions) { + this.failureCooldownMs = options.failureCooldownMs; + this.states = apiKeys.map((apiKey, index) => ({ + apiKey, + keyId: `gemini-key-${index + 1}`, + successCount: 0, + failureCount: 0, + cooldownUntilEpochMs: 0, + })); + } + + acquireKey(nowEpochMs: number = Date.now()): AcquiredProviderKey { + const size = this.states.length; + if (size === 0) { + throw new Error('No provider API keys configured.'); + } + + for (let offset = 0; offset < size; offset += 1) { + const index = (this.nextIndex + offset) % size; + const state = this.states[index]; + + if (state.cooldownUntilEpochMs <= nowEpochMs) { + this.nextIndex = (index + 1) % size; + return { apiKey: state.apiKey, keyId: state.keyId }; + } + } + + const nextAvailableInMs = Math.min( + ...this.states.map((state) => Math.max(0, state.cooldownUntilEpochMs - nowEpochMs)) + ); + + throw new Error(`No provider API keys available. Retry in ~${nextAvailableInMs}ms.`); + } + + reportSuccess(keyId: string): void { + const state = this.findByKeyId(keyId); + if (!state) return; + + state.successCount += 1; + state.cooldownUntilEpochMs = 0; + } + + reportFailure(keyId: string, nowEpochMs: number = Date.now()): void { + const state = this.findByKeyId(keyId); + if (!state) return; + + state.failureCount += 1; + state.cooldownUntilEpochMs = nowEpochMs + this.failureCooldownMs; + } + + getSnapshot(nowEpochMs: number = Date.now()): KeyPoolSnapshot { + const keys: KeyPoolSnapshotItem[] = this.states.map((state) => ({ + keyId: state.keyId, + successCount: state.successCount, + failureCount: state.failureCount, + cooldownRemainingMs: Math.max(0, state.cooldownUntilEpochMs - nowEpochMs), + })); + + return { + configuredKeyCount: this.states.length, + availableKeyCount: keys.filter((entry) => entry.cooldownRemainingMs === 0).length, + failureCooldownMs: this.failureCooldownMs, + keys, + }; + } + + private findByKeyId(keyId: string): ProviderKeyState | undefined { + return this.states.find((state) => state.keyId === keyId); + } +} diff --git a/all-model-chat/apps/bff/src/routes/chatStream.ts b/all-model-chat/apps/bff/src/routes/chatStream.ts new file mode 100644 index 00000000..54c3a834 --- /dev/null +++ b/all-model-chat/apps/bff/src/routes/chatStream.ts @@ -0,0 +1,543 @@ +import { IncomingMessage, ServerResponse } from 'node:http'; +import { GoogleGenAI, Part, UsageMetadata } from '@google/genai'; +import { GeminiProviderClient } from '../providers/geminiClient.js'; +import type { ApiErrorPayload, ChatHistoryTurn, ChatStreamRequestPayload } from '@all-model-chat/shared-api'; +import type { ChatRole } from '@all-model-chat/shared-types'; + +interface ValidationErrorShape { + code: string; + message: string; + status: number; +} + +const MAX_REQUEST_BYTES = 2 * 1024 * 1024; +const JSON_HEADERS = { 'content-type': 'application/json; charset=utf-8' }; +const SSE_HEADERS = { + 'content-type': 'text/event-stream; charset=utf-8', + 'cache-control': 'no-cache, no-transform', + connection: 'keep-alive', + 'x-accel-buffering': 'no', +}; + +class ValidationError extends Error { + constructor(public readonly detail: ValidationErrorShape) { + super(detail.message); + this.name = 'ValidationError'; + } +} + +const isObject = (value: unknown): value is Record => { + return typeof value === 'object' && value !== null; +}; + +const normalizeThoughtSignaturePart = (part: Part): Part => { + const anyPart = part as any; + const thoughtSignature = + anyPart.thoughtSignature || + anyPart.thought_signature || + anyPart.functionCall?.thoughtSignature || + anyPart.functionCall?.thought_signature; + + if (!thoughtSignature) return part; + + return { + ...part, + thoughtSignature, + thought_signature: thoughtSignature, + } as any; +}; + +const pushUniqueCitations = (groundingMetadata: Record, newCitations: unknown): void => { + if (!Array.isArray(newCitations)) return; + + const existing = Array.isArray(groundingMetadata.citations) + ? (groundingMetadata.citations as Array>) + : []; + + for (const citation of newCitations) { + if (!isObject(citation)) continue; + + const uri = typeof citation.uri === 'string' ? citation.uri : null; + if (!uri) continue; + + if (!existing.some((current) => current.uri === uri)) { + existing.push(citation); + } + } + + groundingMetadata.citations = existing; +}; + +const writeSseEvent = (response: ServerResponse, eventName: string, data: unknown): void => { + if (response.writableEnded || response.destroyed) return; + + response.write(`event: ${eventName}\n`); + response.write(`data: ${JSON.stringify(data)}\n\n`); +}; + +const readRequestBody = async (request: IncomingMessage): Promise => { + return new Promise((resolve, reject) => { + let body = ''; + let totalBytes = 0; + let isCompleted = false; + + request.setEncoding('utf8'); + + request.on('data', (chunk: string) => { + if (isCompleted) return; + + totalBytes += Buffer.byteLength(chunk); + if (totalBytes > MAX_REQUEST_BYTES) { + isCompleted = true; + reject( + new ValidationError({ + code: 'payload_too_large', + message: `Request body exceeds ${MAX_REQUEST_BYTES} bytes.`, + status: 413, + }) + ); + request.destroy(); + return; + } + + body += chunk; + }); + + request.on('end', () => { + if (isCompleted) return; + isCompleted = true; + resolve(body); + }); + + request.on('aborted', () => { + if (isCompleted) return; + isCompleted = true; + reject( + new ValidationError({ + code: 'request_aborted', + message: 'Request was aborted by the client.', + status: 499, + }) + ); + }); + + request.on('error', (error) => { + if (isCompleted) return; + isCompleted = true; + reject(error); + }); + }); +}; + +const normalizePartArray = (input: unknown, fieldPath: string, required: boolean): Part[] => { + if (input === undefined) { + if (required) { + throw new ValidationError({ + code: 'invalid_request', + message: `${fieldPath} is required.`, + status: 400, + }); + } + return []; + } + + if (!Array.isArray(input)) { + throw new ValidationError({ + code: 'invalid_request', + message: `${fieldPath} must be an array.`, + status: 400, + }); + } + + for (let index = 0; index < input.length; index += 1) { + if (!isObject(input[index])) { + throw new ValidationError({ + code: 'invalid_request', + message: `${fieldPath}[${index}] must be an object.`, + status: 400, + }); + } + } + + return input as Part[]; +}; + +const normalizeHistory = (input: unknown): ChatHistoryTurn[] => { + if (input === undefined) return []; + + if (!Array.isArray(input)) { + throw new ValidationError({ + code: 'invalid_request', + message: '`history` must be an array.', + status: 400, + }); + } + + return input.map((item, index) => { + if (!isObject(item)) { + throw new ValidationError({ + code: 'invalid_request', + message: `history[${index}] must be an object.`, + status: 400, + }); + } + + const roleRaw = item.role; + if (roleRaw !== 'user' && roleRaw !== 'model') { + throw new ValidationError({ + code: 'invalid_request', + message: `history[${index}].role must be "user" or "model".`, + status: 400, + }); + } + + return { + role: roleRaw, + parts: normalizePartArray(item.parts, `history[${index}].parts`, true), + }; + }); +}; + +const parseChatStreamPayload = (rawBody: string): ChatStreamRequestPayload => { + if (!rawBody.trim()) { + throw new ValidationError({ + code: 'invalid_request', + message: 'Request body is required.', + status: 400, + }); + } + + let parsed: unknown; + try { + parsed = JSON.parse(rawBody); + } catch { + throw new ValidationError({ + code: 'invalid_json', + message: 'Request body must be valid JSON.', + status: 400, + }); + } + + if (!isObject(parsed)) { + throw new ValidationError({ + code: 'invalid_request', + message: 'Request body must be a JSON object.', + status: 400, + }); + } + + const modelRaw = parsed.model; + const model = typeof modelRaw === 'string' ? modelRaw.trim() : ''; + if (!model) { + throw new ValidationError({ + code: 'invalid_request', + message: '`model` must be a non-empty string.', + status: 400, + }); + } + + const roleRaw = parsed.role; + const role: ChatRole = roleRaw === 'model' ? 'model' : 'user'; + const apiKeyOverrideRaw = parsed.apiKeyOverride; + let apiKeyOverride: string | undefined; + if (typeof apiKeyOverrideRaw === 'string') { + const trimmed = apiKeyOverrideRaw.trim(); + if (trimmed.length > 0) { + apiKeyOverride = trimmed; + } + } + const history = normalizeHistory(parsed.history); + const parts = normalizePartArray(parsed.parts, '`parts`', false); + + if (history.length === 0 && parts.length === 0) { + throw new ValidationError({ + code: 'invalid_request', + message: 'Either `history` or `parts` must contain at least one item.', + status: 400, + }); + } + + return { + model, + history, + parts, + config: parsed.config, + role, + apiKeyOverride, + }; +}; + +const mapValidationError = (error: unknown): ValidationErrorShape => { + if (error instanceof ValidationError) { + return error.detail; + } + + return { + code: 'invalid_request', + message: error instanceof Error ? error.message : 'Failed to parse request.', + status: 400, + }; +}; + +const readNumericStatus = (error: unknown): number | null => { + if (!isObject(error)) return null; + + const status = error.status; + if (typeof status === 'number' && Number.isFinite(status)) return status; + + const statusCode = error.statusCode; + if (typeof statusCode === 'number' && Number.isFinite(statusCode)) return statusCode; + + return null; +}; + +const mapProviderError = (error: unknown): ApiErrorPayload => { + const status = readNumericStatus(error) ?? 500; + const message = error instanceof Error ? error.message : 'Provider stream failed.'; + + if (message.includes('No provider API keys configured.')) { + return { + code: 'provider_key_not_configured', + message, + status: 503, + retryable: false, + }; + } + if (message.includes('No provider API keys available.')) { + return { + code: 'provider_key_temporarily_unavailable', + message, + status: 503, + retryable: true, + }; + } + + if (status === 400) { + return { code: 'provider_invalid_request', message, status, retryable: false }; + } + if (status === 401) { + return { code: 'provider_auth_failed', message, status, retryable: false }; + } + if (status === 403) { + return { code: 'provider_forbidden', message, status, retryable: false }; + } + if (status === 404) { + return { code: 'provider_not_found', message, status, retryable: false }; + } + if (status === 408) { + return { code: 'provider_timeout', message, status, retryable: true }; + } + if (status === 429) { + return { code: 'provider_rate_limited', message, status, retryable: true }; + } + if (status >= 500 && status <= 599) { + return { code: 'provider_upstream_error', message, status, retryable: true }; + } + + return { + code: 'provider_unknown_error', + message, + status, + retryable: status >= 500 || status === 429, + }; +}; + +export const handleChatStreamRoute = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + let payload: ChatStreamRequestPayload; + try { + const rawBody = await readRequestBody(request); + payload = parseChatStreamPayload(rawBody); + } catch (error) { + const mapped = mapValidationError(error); + response.writeHead(mapped.status, JSON_HEADERS); + response.end(JSON.stringify({ error: mapped })); + return; + } + + const contents: ChatHistoryTurn[] = + payload.parts.length > 0 + ? [...payload.history, { role: payload.role, parts: payload.parts }] + : payload.history; + + response.writeHead(200, SSE_HEADERS); + if (typeof response.flushHeaders === 'function') { + response.flushHeaders(); + } + writeSseEvent(response, 'ready', { ok: true }); + + const abortController = new AbortController(); + const onClientDisconnect = () => { + if (!abortController.signal.aborted) { + abortController.abort(); + } + }; + + request.on('aborted', onClientDisconnect); + response.on('close', onClientDisconnect); + + try { + const streamWithClient = async (client: GoogleGenAI, keyId: string): Promise => { + writeSseEvent(response, 'meta', { + provider: 'gemini', + keyId, + }); + + let finalUsageMetadata: UsageMetadata | undefined; + let finalGroundingMetadata: Record | undefined; + let finalUrlContextMetadata: unknown = undefined; + let detectedFunctionCallPart: Part | undefined = undefined; + let latestToolCallFunction: unknown = undefined; + let latestToolCallSignature: string | undefined = undefined; + let latestThoughtSignatureFromParts: string | undefined = undefined; + + const result = await client.models.generateContentStream({ + model: payload.model, + contents, + config: payload.config as any, + }); + + for await (const chunkResponse of result) { + if (abortController.signal.aborted) { + break; + } + + if (chunkResponse.usageMetadata) { + finalUsageMetadata = chunkResponse.usageMetadata; + } + + const candidate = chunkResponse.candidates?.[0]; + if (!candidate) { + continue; + } + + if (candidate.groundingMetadata) { + finalGroundingMetadata = { ...(candidate.groundingMetadata as Record) }; + } + + const anyCandidate = candidate as any; + const urlMetadata = anyCandidate.urlContextMetadata || anyCandidate.url_context_metadata; + if (urlMetadata) { + finalUrlContextMetadata = urlMetadata; + } + + const toolCalls = anyCandidate.toolCalls as any[] | undefined; + if (toolCalls) { + for (const toolCall of toolCalls) { + if (toolCall.functionCall?.args?.urlContextMetadata) { + if (!finalGroundingMetadata) { + finalGroundingMetadata = {}; + } + pushUniqueCitations( + finalGroundingMetadata, + toolCall.functionCall.args.urlContextMetadata.citations + ); + } + + if (toolCall.functionCall) { + latestToolCallFunction = toolCall.functionCall; + const anyToolCall = toolCall as any; + latestToolCallSignature = + anyToolCall.thoughtSignature || + anyToolCall.thought_signature || + anyToolCall.functionCall?.thoughtSignature || + anyToolCall.functionCall?.thought_signature; + } + } + } + + const candidateParts = candidate.content?.parts; + if (!candidateParts?.length) { + continue; + } + + for (const part of candidateParts) { + const anyPart = part as any; + const partSignature = anyPart.thoughtSignature || anyPart.thought_signature; + if (partSignature) { + latestThoughtSignatureFromParts = partSignature; + } + + if (anyPart.functionCall) { + detectedFunctionCallPart = normalizeThoughtSignaturePart(part); + continue; + } + + if (anyPart.thought) { + writeSseEvent(response, 'thought', { chunk: part.text || '' }); + continue; + } + + writeSseEvent(response, 'part', { part }); + } + } + + if (!detectedFunctionCallPart && latestToolCallFunction) { + detectedFunctionCallPart = { + functionCall: latestToolCallFunction as Part['functionCall'], + ...(latestToolCallSignature || latestThoughtSignatureFromParts + ? { + thoughtSignature: latestToolCallSignature || latestThoughtSignatureFromParts, + thought_signature: latestToolCallSignature || latestThoughtSignatureFromParts, + } + : {}), + } as any; + } else if (detectedFunctionCallPart && (latestToolCallSignature || latestThoughtSignatureFromParts)) { + const anyPart = detectedFunctionCallPart as any; + if (!anyPart.thoughtSignature && !anyPart.thought_signature) { + detectedFunctionCallPart = { + ...detectedFunctionCallPart, + thoughtSignature: latestToolCallSignature || latestThoughtSignatureFromParts, + thought_signature: latestToolCallSignature || latestThoughtSignatureFromParts, + } as any; + } + } + + if (!abortController.signal.aborted) { + writeSseEvent(response, 'complete', { + usageMetadata: finalUsageMetadata, + groundingMetadata: finalGroundingMetadata, + urlContextMetadata: finalUrlContextMetadata, + functionCallPart: detectedFunctionCallPart, + }); + } + }; + + if (payload.apiKeyOverride) { + const providerConfig = geminiProviderClient.getProviderConfigSnapshot(); + const clientOptions: Record = { + apiKey: payload.apiKeyOverride, + vertexai: providerConfig.useVertexAi, + }; + if (providerConfig.apiVersion) { + clientOptions.apiVersion = providerConfig.apiVersion; + } + if (providerConfig.baseUrl) { + clientOptions.httpOptions = { + baseUrl: providerConfig.baseUrl, + }; + } + + const overrideClient = new GoogleGenAI(clientOptions as any); + await streamWithClient(overrideClient, 'custom-key'); + } else { + await geminiProviderClient.withClient(async ({ client, keyId }) => { + await streamWithClient(client, keyId); + }); + } + } catch (error) { + if (!abortController.signal.aborted) { + writeSseEvent(response, 'error', { error: mapProviderError(error) }); + } + } finally { + request.off('aborted', onClientDisconnect); + response.off('close', onClientDisconnect); + } + + if (!response.writableEnded && !response.destroyed) { + response.end(); + } +}; diff --git a/all-model-chat/apps/bff/src/routes/files.ts b/all-model-chat/apps/bff/src/routes/files.ts new file mode 100644 index 00000000..afb8655d --- /dev/null +++ b/all-model-chat/apps/bff/src/routes/files.ts @@ -0,0 +1,122 @@ +import { IncomingMessage, ServerResponse } from 'node:http'; +import { GeminiProviderClient } from '../providers/geminiClient.js'; +import type { FileMetadataResponse, FileUploadResponse } from '@all-model-chat/shared-api'; +import { + RequestValidationError, + mapProviderError, + parseRequestUrl, + readBinaryBody, + sendJson, +} from './routeCommon.js'; + +const isFileNotFoundError = (error: unknown): boolean => { + const message = error instanceof Error ? error.message : ''; + return message.includes('NOT_FOUND') || message.includes('404'); +}; + +const handleFileUpload = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const requestUrl = parseRequestUrl(request); + const displayName = requestUrl.searchParams.get('displayName')?.trim() || ''; + const mimeType = requestUrl.searchParams.get('mimeType')?.trim() || ''; + + if (!displayName) { + throw new RequestValidationError('invalid_request', 400, '`displayName` query parameter is required.'); + } + if (!mimeType) { + throw new RequestValidationError('invalid_request', 400, '`mimeType` query parameter is required.'); + } + + const body = await readBinaryBody(request, 64 * 1024 * 1024); + if (body.length === 0) { + throw new RequestValidationError('invalid_request', 400, 'Upload body is empty.'); + } + + const uploadedFile = await geminiProviderClient.withClient(async ({ client }) => { + const fileBlob = new Blob([new Uint8Array(body)], { type: mimeType }); + return client.files.upload({ + file: fileBlob, + config: { + displayName, + mimeType, + }, + }); + }); + + const payload: FileUploadResponse = { file: uploadedFile }; + sendJson(response, 200, payload); +}; + +const handleFileMetadata = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const requestUrl = parseRequestUrl(request); + const name = requestUrl.searchParams.get('name')?.trim() || ''; + + if (!name) { + throw new RequestValidationError('invalid_request', 400, '`name` query parameter is required.'); + } + + try { + const file = await geminiProviderClient.withClient(async ({ client }) => { + return client.files.get({ name }); + }); + + const payload: FileMetadataResponse = { file }; + sendJson(response, 200, payload); + } catch (error) { + if (isFileNotFoundError(error)) { + const payload: FileMetadataResponse = { file: null }; + sendJson(response, 200, payload); + return; + } + + throw error; + } +}; + +export const handleFilesRoute = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const method = request.method || 'GET'; + const path = (request.url || '/').split('?')[0]; + + if (path === '/api/files/upload') { + if (method !== 'POST') { + sendJson(response, 405, { error: { code: 'method_not_allowed', message: 'Method Not Allowed', status: 405 } }); + return true; + } + + try { + await handleFileUpload(request, response, geminiProviderClient); + } catch (error) { + const mapped = mapProviderError(error); + sendJson(response, mapped.status, { error: mapped }); + } + return true; + } + + if (path === '/api/files/metadata') { + if (method !== 'GET') { + sendJson(response, 405, { error: { code: 'method_not_allowed', message: 'Method Not Allowed', status: 405 } }); + return true; + } + + try { + await handleFileMetadata(request, response, geminiProviderClient); + } catch (error) { + const mapped = mapProviderError(error); + sendJson(response, mapped.status, { error: mapped }); + } + return true; + } + + return false; +}; diff --git a/all-model-chat/apps/bff/src/routes/generation.ts b/all-model-chat/apps/bff/src/routes/generation.ts new file mode 100644 index 00000000..e2f233fa --- /dev/null +++ b/all-model-chat/apps/bff/src/routes/generation.ts @@ -0,0 +1,620 @@ +import { IncomingMessage, ServerResponse } from 'node:http'; +import { Part, Type } from '@google/genai'; +import { GeminiProviderClient } from '../providers/geminiClient.js'; +import type { + CountTokensRequest, + EditImageRequest, + ImageGenerationRequest, + SpeechGenerationRequest, + SuggestionsRequest, + TitleRequest, + TranscribeAudioRequest, + TranslateRequest, +} from '@all-model-chat/shared-api'; +import { + RequestValidationError, + isObject, + mapProviderError, + readJsonBody, + sendJson, +} from './routeCommon.js'; + +const parseString = ( + value: unknown, + fieldName: string, + options?: { allowEmpty?: boolean } +): string => { + if (typeof value !== 'string') { + throw new RequestValidationError('invalid_request', 400, `\`${fieldName}\` must be a string.`); + } + + const normalized = value.trim(); + if (!options?.allowEmpty && normalized.length === 0) { + throw new RequestValidationError('invalid_request', 400, `\`${fieldName}\` must not be empty.`); + } + + return normalized; +}; + +const parseOptionalString = (value: unknown): string | undefined => { + if (typeof value !== 'string') return undefined; + const normalized = value.trim(); + return normalized.length > 0 ? normalized : undefined; +}; + +const parsePartArray = (value: unknown, fieldName: string): Part[] => { + if (!Array.isArray(value)) { + throw new RequestValidationError('invalid_request', 400, `\`${fieldName}\` must be an array.`); + } + + for (let index = 0; index < value.length; index += 1) { + if (!isObject(value[index])) { + throw new RequestValidationError( + 'invalid_request', + 400, + `\`${fieldName}[${index}]\` must be an object.` + ); + } + } + + return value as Part[]; +}; + +const parseHistoryArray = (value: unknown, fieldName: string): Array<{ role: 'user' | 'model'; parts: Part[] }> => { + if (!Array.isArray(value)) { + throw new RequestValidationError('invalid_request', 400, `\`${fieldName}\` must be an array.`); + } + + return value.map((entry, index) => { + if (!isObject(entry)) { + throw new RequestValidationError('invalid_request', 400, `\`${fieldName}[${index}]\` must be an object.`); + } + + const role = entry.role; + if (role !== 'user' && role !== 'model') { + throw new RequestValidationError( + 'invalid_request', + 400, + `\`${fieldName}[${index}].role\` must be \"user\" or \"model\".` + ); + } + + return { + role, + parts: parsePartArray(entry.parts, `${fieldName}[${index}].parts`), + }; + }); +}; + +const parseImageGenerationRequest = (payload: unknown): ImageGenerationRequest => { + if (!isObject(payload)) { + throw new RequestValidationError('invalid_request', 400, 'Request body must be a JSON object.'); + } + + return { + model: parseString(payload.model, 'model'), + prompt: parseString(payload.prompt, 'prompt'), + aspectRatio: parseString(payload.aspectRatio, 'aspectRatio'), + imageSize: parseOptionalString(payload.imageSize), + }; +}; + +const parseSpeechGenerationRequest = (payload: unknown): SpeechGenerationRequest => { + if (!isObject(payload)) { + throw new RequestValidationError('invalid_request', 400, 'Request body must be a JSON object.'); + } + + return { + model: parseString(payload.model, 'model'), + text: parseString(payload.text, 'text'), + voice: parseString(payload.voice, 'voice'), + }; +}; + +const parseTranscribeRequest = (payload: unknown): TranscribeAudioRequest => { + if (!isObject(payload)) { + throw new RequestValidationError('invalid_request', 400, 'Request body must be a JSON object.'); + } + + return { + model: parseString(payload.model, 'model'), + mimeType: parseString(payload.mimeType, 'mimeType'), + audioBase64: parseString(payload.audioBase64, 'audioBase64'), + }; +}; + +const parseTranslateRequest = (payload: unknown): TranslateRequest => { + if (!isObject(payload)) { + throw new RequestValidationError('invalid_request', 400, 'Request body must be a JSON object.'); + } + + return { + text: parseString(payload.text, 'text'), + targetLanguage: parseOptionalString(payload.targetLanguage), + }; +}; + +const parseTitleRequest = (payload: unknown): TitleRequest => { + if (!isObject(payload)) { + throw new RequestValidationError('invalid_request', 400, 'Request body must be a JSON object.'); + } + + const language = payload.language === 'zh' ? 'zh' : 'en'; + + return { + userContent: parseString(payload.userContent, 'userContent'), + modelContent: parseString(payload.modelContent, 'modelContent'), + language, + }; +}; + +const parseSuggestionsRequest = (payload: unknown): SuggestionsRequest => { + if (!isObject(payload)) { + throw new RequestValidationError('invalid_request', 400, 'Request body must be a JSON object.'); + } + + const language = payload.language === 'zh' ? 'zh' : 'en'; + + return { + userContent: parseString(payload.userContent, 'userContent'), + modelContent: parseString(payload.modelContent, 'modelContent'), + language, + }; +}; + +const parseCountTokensRequest = (payload: unknown): CountTokensRequest => { + if (!isObject(payload)) { + throw new RequestValidationError('invalid_request', 400, 'Request body must be a JSON object.'); + } + + return { + model: parseString(payload.model, 'model'), + parts: parsePartArray(payload.parts, 'parts'), + }; +}; + +const parseEditImageRequest = (payload: unknown): EditImageRequest => { + if (!isObject(payload)) { + throw new RequestValidationError('invalid_request', 400, 'Request body must be a JSON object.'); + } + + return { + model: parseString(payload.model, 'model'), + history: parseHistoryArray(payload.history || [], 'history'), + parts: parsePartArray(payload.parts || [], 'parts'), + aspectRatio: parseOptionalString(payload.aspectRatio), + imageSize: parseOptionalString(payload.imageSize), + }; +}; + +const parseSuggestionsResponse = (responseText: string): string[] => { + const parsed = JSON.parse(responseText); + if (!parsed || !Array.isArray(parsed.suggestions)) { + throw new Error('Suggestions response did not contain `suggestions` array.'); + } + + return parsed.suggestions.filter((entry: unknown) => typeof entry === 'string').slice(0, 3); +}; + +const handleGenerateImages = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const parsed = parseImageGenerationRequest(await readJsonBody(request)); + + const images = await geminiProviderClient.withClient(async ({ client }) => { + const config: Record = { + numberOfImages: 1, + outputMimeType: 'image/png', + aspectRatio: parsed.aspectRatio, + }; + + if (parsed.imageSize) { + config.imageSize = parsed.imageSize; + } + + const result = await client.models.generateImages({ + model: parsed.model, + prompt: parsed.prompt, + config, + }); + + return result.generatedImages?.map((item) => item.image?.imageBytes).filter((item): item is string => !!item) || []; + }); + + if (images.length === 0) { + throw new RequestValidationError( + 'provider_empty_response', + 502, + 'No images were generated by upstream provider.' + ); + } + + sendJson(response, 200, { images }); +}; + +const handleGenerateSpeech = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const parsed = parseSpeechGenerationRequest(await readJsonBody(request)); + + const audioData = await geminiProviderClient.withClient(async ({ client }) => { + const result = await client.models.generateContent({ + model: parsed.model, + contents: [{ parts: [{ text: parsed.text }] }], + config: { + responseModalities: ['AUDIO'], + speechConfig: { + voiceConfig: { prebuiltVoiceConfig: { voiceName: parsed.voice } }, + }, + }, + }); + + return result.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data; + }); + + if (!audioData) { + throw new RequestValidationError('provider_empty_response', 502, 'No audio data returned by upstream provider.'); + } + + sendJson(response, 200, { audioData }); +}; + +const buildTranscribeConfig = (modelId: string): Record => { + const config: Record = { + systemInstruction: + '请准确转录语音内容。使用正确的标点符号。不要描述音频、回答问题或添加对话填充词,仅返回文本。若音频中无语音或仅有背景噪音,请不要输出任何文字。', + }; + + if (modelId.includes('gemini-3')) { + config.thinkingConfig = { + includeThoughts: false, + thinkingLevel: 'MINIMAL', + }; + } else if (modelId === 'gemini-2.5-pro') { + config.thinkingConfig = { thinkingBudget: 128 }; + } else if (modelId.includes('flash')) { + config.thinkingConfig = { thinkingBudget: 512 }; + } else { + config.thinkingConfig = { thinkingBudget: 0 }; + } + + return config; +}; + +const handleTranscribeAudio = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const parsed = parseTranscribeRequest(await readJsonBody(request, 16 * 1024 * 1024)); + + const text = await geminiProviderClient.withClient(async ({ client }) => { + const result = await client.models.generateContent({ + model: parsed.model, + contents: { + parts: [ + { text: 'Transcribe audio.' }, + { + inlineData: { + mimeType: parsed.mimeType, + data: parsed.audioBase64, + }, + }, + ], + }, + config: buildTranscribeConfig(parsed.model), + }); + + return result.text?.trim() || ''; + }); + + if (!text) { + throw new RequestValidationError('provider_empty_response', 502, 'Transcription returned empty response.'); + } + + sendJson(response, 200, { text }); +}; + +const handleTranslate = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const parsed = parseTranslateRequest(await readJsonBody(request)); + const targetLanguage = parsed.targetLanguage || 'English'; + const prompt = `Translate the following text to ${targetLanguage}. Only return the translated text, without any additional explanation or formatting.\n\nText to translate:\n"""\n${parsed.text}\n"""`; + + const text = await geminiProviderClient.withClient(async ({ client }) => { + const result = await client.models.generateContent({ + model: 'gemini-2.5-flash-lite', + contents: prompt, + config: { + temperature: 0.1, + topP: 0.95, + thinkingConfig: { thinkingBudget: -1 }, + }, + }); + + return result.text?.trim() || ''; + }); + + if (!text) { + throw new RequestValidationError('provider_empty_response', 502, 'Translation returned empty response.'); + } + + sendJson(response, 200, { text }); +}; + +const buildSuggestionsPrompt = (input: SuggestionsRequest): string => { + if (input.language === 'zh') { + return `作为对话专家,请基于以下上下文,预测用户接下来最可能发送的 3 条简短回复。 + +规则: +1. 如果助手最后在提问,建议必须是针对该问题的回答。 +2. 建议应简练(20字以内),涵盖不同角度(如:追问细节、请求示例、或提出质疑)。 +3. 语气自然,符合人类对话习惯。 + +对话上下文: +用户: "${input.userContent}" +助手: "${input.modelContent}"`; + } + + return `As a conversation expert, predict the 3 most likely short follow-up messages the USER would send based on the context below. + +Context: +USER: "${input.userContent}" +ASSISTANT: "${input.modelContent}"`; +}; + +const handleGenerateSuggestions = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const parsed = parseSuggestionsRequest(await readJsonBody(request)); + const prompt = buildSuggestionsPrompt(parsed); + + let suggestions: string[] = []; + + try { + suggestions = await geminiProviderClient.withClient(async ({ client }) => { + const result = await client.models.generateContent({ + model: 'gemini-2.5-flash-lite', + contents: prompt, + config: { + thinkingConfig: { thinkingBudget: -1 }, + temperature: 0.8, + topP: 0.95, + responseMimeType: 'application/json', + responseSchema: { + type: Type.OBJECT, + properties: { + suggestions: { + type: Type.ARRAY, + items: { + type: Type.STRING, + }, + }, + }, + }, + }, + }); + + return parseSuggestionsResponse(result.text?.trim() || ''); + }); + } catch { + suggestions = await geminiProviderClient.withClient(async ({ client }) => { + const fallbackPrompt = `${prompt}\n\nReturn the three suggestions as a numbered list, one per line. Do not include any other text or formatting.`; + const result = await client.models.generateContent({ + model: 'gemini-2.5-flash-lite', + contents: fallbackPrompt, + config: { + thinkingConfig: { thinkingBudget: -1 }, + temperature: 0.8, + topP: 0.95, + }, + }); + + return (result.text || '') + .trim() + .split('\n') + .map((entry) => entry.replace(/^\d+\.\s*/, '').trim()) + .filter((entry) => entry.length > 0) + .slice(0, 3); + }); + } + + sendJson(response, 200, { suggestions }); +}; + +const handleGenerateTitle = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const parsed = parseTitleRequest(await readJsonBody(request)); + const prompt = + parsed.language === 'zh' + ? `根据以下对话,创建一个非常简短、简洁的标题(最多4-6个词)。不要使用引号或任何其他格式。只返回标题的文本。\n\n用户: "${parsed.userContent}"\n助手: "${parsed.modelContent}"\n\n标题:` + : `Based on this conversation, create a very short, concise title (4-6 words max). Do not use quotes or any other formatting. Just return the text of the title.\n\nUSER: "${parsed.userContent}"\nASSISTANT: "${parsed.modelContent}"\n\nTITLE:`; + + const title = await geminiProviderClient.withClient(async ({ client }) => { + const result = await client.models.generateContent({ + model: 'gemini-2.5-flash-lite', + contents: prompt, + config: { + thinkingConfig: { thinkingBudget: -1 }, + temperature: 0.3, + topP: 0.9, + }, + }); + + let value = result.text?.trim() || ''; + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + return value; + }); + + if (!title) { + throw new RequestValidationError('provider_empty_response', 502, 'Title generation returned empty response.'); + } + + sendJson(response, 200, { title }); +}; + +const handleCountTokens = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const parsed = parseCountTokensRequest(await readJsonBody(request, 4 * 1024 * 1024)); + + const sanitizedParts = parsed.parts.map((part) => { + const { mediaResolution, videoMetadata, thoughtSignature, thought_signature, ...rest } = part as any; + return rest as Part; + }); + + const totalTokens = await geminiProviderClient.withClient(async ({ client }) => { + const result = await client.models.countTokens({ + model: parsed.model, + contents: [{ role: 'user', parts: sanitizedParts }], + }); + return result.totalTokens || 0; + }); + + sendJson(response, 200, { totalTokens }); +}; + +const normalizeResponsePart = (part: Part): Part => { + const anyPart = part as any; + const thoughtSignature = + anyPart.thoughtSignature || + anyPart.thought_signature || + anyPart.functionCall?.thoughtSignature || + anyPart.functionCall?.thought_signature; + + if (!thoughtSignature) return part; + + return { + ...part, + thoughtSignature, + thought_signature: thoughtSignature, + } as any; +}; + +const handleEditImage = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const parsed = parseEditImageRequest(await readJsonBody(request, 8 * 1024 * 1024)); + + const config: Record = { + responseModalities: ['IMAGE', 'TEXT'], + }; + + const imageConfig: Record = {}; + if (parsed.aspectRatio && parsed.aspectRatio !== 'Auto') { + imageConfig.aspectRatio = parsed.aspectRatio; + } + if (parsed.model === 'gemini-3-pro-image-preview' && parsed.imageSize) { + imageConfig.imageSize = parsed.imageSize; + } + if (Object.keys(imageConfig).length > 0) { + config.imageConfig = imageConfig; + } + + const resultParts = await geminiProviderClient.withClient(async ({ client }) => { + const providerResponse = await client.models.generateContent({ + model: parsed.model, + contents: [...parsed.history, { role: 'user', parts: parsed.parts }], + config, + }); + + const parts: Part[] = []; + const candidateParts = providerResponse.candidates?.[0]?.content?.parts || []; + for (const part of candidateParts) { + const asThought = part as any; + if (asThought.thought) { + continue; + } + parts.push(normalizeResponsePart(part)); + } + + if (parts.length === 0 && providerResponse.text) { + parts.push({ text: providerResponse.text }); + } + + return parts; + }); + + sendJson(response, 200, { parts: resultParts }); +}; + +export const handleGenerationRoute = async ( + request: IncomingMessage, + response: ServerResponse, + geminiProviderClient: GeminiProviderClient +): Promise => { + const method = request.method || 'GET'; + const path = (request.url || '/').split('?')[0]; + + if (!path.startsWith('/api/generation/')) { + return false; + } + + if (method !== 'POST') { + sendJson(response, 405, { error: { code: 'method_not_allowed', message: 'Method Not Allowed', status: 405 } }); + return true; + } + + try { + if (path === '/api/generation/images') { + await handleGenerateImages(request, response, geminiProviderClient); + return true; + } + if (path === '/api/generation/speech') { + await handleGenerateSpeech(request, response, geminiProviderClient); + return true; + } + if (path === '/api/generation/transcribe') { + await handleTranscribeAudio(request, response, geminiProviderClient); + return true; + } + if (path === '/api/generation/translate') { + await handleTranslate(request, response, geminiProviderClient); + return true; + } + if (path === '/api/generation/title') { + await handleGenerateTitle(request, response, geminiProviderClient); + return true; + } + if (path === '/api/generation/suggestions') { + await handleGenerateSuggestions(request, response, geminiProviderClient); + return true; + } + if (path === '/api/generation/count-tokens') { + await handleCountTokens(request, response, geminiProviderClient); + return true; + } + if (path === '/api/generation/edit-image') { + await handleEditImage(request, response, geminiProviderClient); + return true; + } + + sendJson(response, 404, { error: { code: 'not_found', message: 'Not Found', status: 404 } }); + return true; + } catch (error) { + const mapped = mapProviderError(error); + sendJson(response, mapped.status, { error: mapped }); + return true; + } +}; diff --git a/all-model-chat/apps/bff/src/routes/health.ts b/all-model-chat/apps/bff/src/routes/health.ts new file mode 100644 index 00000000..7314e78b --- /dev/null +++ b/all-model-chat/apps/bff/src/routes/health.ts @@ -0,0 +1,26 @@ +import { BffConfig } from '../config/env.js'; +import { KeyPoolSnapshot } from '../providers/keyPool.js'; +import type { GeminiProviderClient } from '../providers/geminiClient.js'; + +export const createHealthPayload = ( + config: BffConfig, + keyPool: KeyPoolSnapshot, + providerConfig: ReturnType +) => { + return { + status: 'ok', + service: config.serviceName, + environment: config.nodeEnv, + timestamp: new Date().toISOString(), + uptimeSeconds: Math.floor(process.uptime()), + provider: { + useVertexAi: providerConfig.useVertexAi, + baseUrl: providerConfig.baseUrl, + apiVersion: providerConfig.apiVersion, + configuredKeyCount: keyPool.configuredKeyCount, + availableKeyCount: keyPool.availableKeyCount, + failureCooldownMs: keyPool.failureCooldownMs, + keys: keyPool.keys, + }, + }; +}; diff --git a/all-model-chat/apps/bff/src/routes/routeCommon.ts b/all-model-chat/apps/bff/src/routes/routeCommon.ts new file mode 100644 index 00000000..93c00a7b --- /dev/null +++ b/all-model-chat/apps/bff/src/routes/routeCommon.ts @@ -0,0 +1,196 @@ +import { IncomingMessage, ServerResponse } from 'node:http'; +import type { ApiErrorPayload } from '@all-model-chat/shared-api'; + +export const JSON_HEADERS = { 'content-type': 'application/json; charset=utf-8' }; + +export class RequestValidationError extends Error { + constructor( + public readonly code: string, + public readonly status: number, + message: string + ) { + super(message); + this.name = 'RequestValidationError'; + } +} + +export const isObject = (value: unknown): value is Record => { + return typeof value === 'object' && value !== null; +}; + +export const sendJson = (response: ServerResponse, status: number, payload: unknown): void => { + response.writeHead(status, JSON_HEADERS); + response.end(JSON.stringify(payload)); +}; + +const readRequestBody = async ( + request: IncomingMessage, + options: { + maxBytes: number; + encoding?: BufferEncoding; + } +): Promise => { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + let totalBytes = 0; + let isCompleted = false; + + if (options.encoding) { + request.setEncoding(options.encoding); + } + + request.on('data', (chunk: Buffer | string) => { + if (isCompleted) return; + + const bufferChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, options.encoding || 'utf8'); + totalBytes += bufferChunk.length; + + if (totalBytes > options.maxBytes) { + isCompleted = true; + reject( + new RequestValidationError( + 'payload_too_large', + 413, + `Request body exceeds ${options.maxBytes} bytes.` + ) + ); + request.destroy(); + return; + } + + chunks.push(bufferChunk); + }); + + request.on('end', () => { + if (isCompleted) return; + isCompleted = true; + const buffer = Buffer.concat(chunks); + if (options.encoding) { + resolve(buffer.toString(options.encoding)); + return; + } + resolve(buffer); + }); + + request.on('aborted', () => { + if (isCompleted) return; + isCompleted = true; + reject(new RequestValidationError('request_aborted', 499, 'Request was aborted by the client.')); + }); + + request.on('error', (error) => { + if (isCompleted) return; + isCompleted = true; + reject(error); + }); + }); +}; + +export const readJsonBody = async ( + request: IncomingMessage, + maxBytes: number = 2 * 1024 * 1024 +): Promise => { + const rawBody = (await readRequestBody(request, { + maxBytes, + encoding: 'utf8', + })) as string; + + if (!rawBody.trim()) { + throw new RequestValidationError('invalid_request', 400, 'Request body is required.'); + } + + try { + return JSON.parse(rawBody); + } catch { + throw new RequestValidationError('invalid_json', 400, 'Request body must be valid JSON.'); + } +}; + +export const readBinaryBody = async ( + request: IncomingMessage, + maxBytes: number = 64 * 1024 * 1024 +): Promise => { + const body = await readRequestBody(request, { maxBytes }); + return body as Buffer; +}; + +const readNumericStatus = (error: unknown): number | null => { + if (!isObject(error)) return null; + + const status = error.status; + if (typeof status === 'number' && Number.isFinite(status)) return status; + + const statusCode = error.statusCode; + if (typeof statusCode === 'number' && Number.isFinite(statusCode)) return statusCode; + + return null; +}; + +export const mapProviderError = (error: unknown): ApiErrorPayload => { + if (error instanceof RequestValidationError) { + return { + code: error.code, + message: error.message, + status: error.status, + retryable: false, + }; + } + + const status = readNumericStatus(error) ?? 500; + const message = error instanceof Error ? error.message : 'Upstream request failed.'; + + if (message.includes('does not support uploading files')) { + return { + code: 'provider_feature_not_supported', + message, + status: 400, + retryable: false, + }; + } + if (message.includes('only supported by the Gemini Developer API')) { + return { + code: 'provider_feature_not_supported', + message, + status: 400, + retryable: false, + }; + } + if (message.includes('No provider API keys configured.')) { + return { code: 'provider_key_not_configured', message, status: 503, retryable: false }; + } + if (message.includes('No provider API keys available.')) { + return { code: 'provider_key_temporarily_unavailable', message, status: 503, retryable: true }; + } + if (status === 400) { + return { code: 'provider_invalid_request', message, status, retryable: false }; + } + if (status === 401) { + return { code: 'provider_auth_failed', message, status, retryable: false }; + } + if (status === 403) { + return { code: 'provider_forbidden', message, status, retryable: false }; + } + if (status === 404) { + return { code: 'provider_not_found', message, status, retryable: false }; + } + if (status === 408) { + return { code: 'provider_timeout', message, status, retryable: true }; + } + if (status === 429) { + return { code: 'provider_rate_limited', message, status, retryable: true }; + } + if (status >= 500 && status <= 599) { + return { code: 'provider_upstream_error', message, status, retryable: true }; + } + + return { + code: 'provider_unknown_error', + message, + status, + retryable: status >= 500 || status === 429, + }; +}; + +export const parseRequestUrl = (request: IncomingMessage): URL => { + return new URL(request.url || '/', 'http://127.0.0.1'); +}; diff --git a/all-model-chat/apps/bff/tsconfig.json b/all-model-chat/apps/bff/tsconfig.json new file mode 100644 index 00000000..3a86fd49 --- /dev/null +++ b/all-model-chat/apps/bff/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "baseUrl": "../..", + "paths": { + "@all-model-chat/shared-api": [ + "packages/shared-api/src/index.d.ts" + ], + "@all-model-chat/shared-types": [ + "packages/shared-types/src/index.d.ts" + ] + }, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "outDir": "dist", + "rootDir": "src", + "types": ["node"] + }, + "include": ["src/**/*.ts"] +} diff --git a/all-model-chat/App.tsx b/all-model-chat/apps/web/App.tsx similarity index 100% rename from all-model-chat/App.tsx rename to all-model-chat/apps/web/App.tsx diff --git a/all-model-chat/components/chat/MessageList.tsx b/all-model-chat/apps/web/components/chat/MessageList.tsx similarity index 100% rename from all-model-chat/components/chat/MessageList.tsx rename to all-model-chat/apps/web/components/chat/MessageList.tsx diff --git a/all-model-chat/components/chat/input/AttachmentMenu.tsx b/all-model-chat/apps/web/components/chat/input/AttachmentMenu.tsx similarity index 98% rename from all-model-chat/components/chat/input/AttachmentMenu.tsx rename to all-model-chat/apps/web/components/chat/input/AttachmentMenu.tsx index 7ae3ffbf..49ee3bfe 100644 --- a/all-model-chat/components/chat/input/AttachmentMenu.tsx +++ b/all-model-chat/apps/web/components/chat/input/AttachmentMenu.tsx @@ -37,7 +37,9 @@ export const AttachmentMenu: React.FC = ({ onAction, disabl const { window: targetWindow } = useWindowContext(); - useClickOutside(containerRef, () => setIsOpen(false), isOpen); + useClickOutside(containerRef, () => { + setIsOpen(false); + }, isOpen); // Prevent click-outside logic from firing when interacting with the portaled menu useEffect(() => { diff --git a/all-model-chat/components/chat/input/ChatInput.tsx b/all-model-chat/apps/web/components/chat/input/ChatInput.tsx similarity index 100% rename from all-model-chat/components/chat/input/ChatInput.tsx rename to all-model-chat/apps/web/components/chat/input/ChatInput.tsx diff --git a/all-model-chat/components/chat/input/ChatInputActions.tsx b/all-model-chat/apps/web/components/chat/input/ChatInputActions.tsx similarity index 100% rename from all-model-chat/components/chat/input/ChatInputActions.tsx rename to all-model-chat/apps/web/components/chat/input/ChatInputActions.tsx diff --git a/all-model-chat/components/chat/input/ChatInputArea.tsx b/all-model-chat/apps/web/components/chat/input/ChatInputArea.tsx similarity index 100% rename from all-model-chat/components/chat/input/ChatInputArea.tsx rename to all-model-chat/apps/web/components/chat/input/ChatInputArea.tsx diff --git a/all-model-chat/components/chat/input/ChatInputFileModals.tsx b/all-model-chat/apps/web/components/chat/input/ChatInputFileModals.tsx similarity index 100% rename from all-model-chat/components/chat/input/ChatInputFileModals.tsx rename to all-model-chat/apps/web/components/chat/input/ChatInputFileModals.tsx diff --git a/all-model-chat/components/chat/input/ChatInputModals.tsx b/all-model-chat/apps/web/components/chat/input/ChatInputModals.tsx similarity index 100% rename from all-model-chat/components/chat/input/ChatInputModals.tsx rename to all-model-chat/apps/web/components/chat/input/ChatInputModals.tsx diff --git a/all-model-chat/components/chat/input/ChatInputToolbar.tsx b/all-model-chat/apps/web/components/chat/input/ChatInputToolbar.tsx similarity index 100% rename from all-model-chat/components/chat/input/ChatInputToolbar.tsx rename to all-model-chat/apps/web/components/chat/input/ChatInputToolbar.tsx diff --git a/all-model-chat/components/chat/input/ChatInputView.tsx b/all-model-chat/apps/web/components/chat/input/ChatInputView.tsx similarity index 100% rename from all-model-chat/components/chat/input/ChatInputView.tsx rename to all-model-chat/apps/web/components/chat/input/ChatInputView.tsx diff --git a/all-model-chat/components/chat/input/LiveStatusBanner.tsx b/all-model-chat/apps/web/components/chat/input/LiveStatusBanner.tsx similarity index 100% rename from all-model-chat/components/chat/input/LiveStatusBanner.tsx rename to all-model-chat/apps/web/components/chat/input/LiveStatusBanner.tsx diff --git a/all-model-chat/components/chat/input/SelectedFileDisplay.tsx b/all-model-chat/apps/web/components/chat/input/SelectedFileDisplay.tsx similarity index 100% rename from all-model-chat/components/chat/input/SelectedFileDisplay.tsx rename to all-model-chat/apps/web/components/chat/input/SelectedFileDisplay.tsx diff --git a/all-model-chat/components/chat/input/SlashCommandMenu.tsx b/all-model-chat/apps/web/components/chat/input/SlashCommandMenu.tsx similarity index 100% rename from all-model-chat/components/chat/input/SlashCommandMenu.tsx rename to all-model-chat/apps/web/components/chat/input/SlashCommandMenu.tsx diff --git a/all-model-chat/components/chat/input/ToolsMenu.tsx b/all-model-chat/apps/web/components/chat/input/ToolsMenu.tsx similarity index 100% rename from all-model-chat/components/chat/input/ToolsMenu.tsx rename to all-model-chat/apps/web/components/chat/input/ToolsMenu.tsx diff --git a/all-model-chat/components/chat/input/actions/LiveControls.tsx b/all-model-chat/apps/web/components/chat/input/actions/LiveControls.tsx similarity index 100% rename from all-model-chat/components/chat/input/actions/LiveControls.tsx rename to all-model-chat/apps/web/components/chat/input/actions/LiveControls.tsx diff --git a/all-model-chat/components/chat/input/actions/RecordControls.tsx b/all-model-chat/apps/web/components/chat/input/actions/RecordControls.tsx similarity index 100% rename from all-model-chat/components/chat/input/actions/RecordControls.tsx rename to all-model-chat/apps/web/components/chat/input/actions/RecordControls.tsx diff --git a/all-model-chat/components/chat/input/actions/SendControls.tsx b/all-model-chat/apps/web/components/chat/input/actions/SendControls.tsx similarity index 100% rename from all-model-chat/components/chat/input/actions/SendControls.tsx rename to all-model-chat/apps/web/components/chat/input/actions/SendControls.tsx diff --git a/all-model-chat/components/chat/input/actions/UtilityControls.tsx b/all-model-chat/apps/web/components/chat/input/actions/UtilityControls.tsx similarity index 100% rename from all-model-chat/components/chat/input/actions/UtilityControls.tsx rename to all-model-chat/apps/web/components/chat/input/actions/UtilityControls.tsx diff --git a/all-model-chat/components/chat/input/actions/WebSearchToggle.tsx b/all-model-chat/apps/web/components/chat/input/actions/WebSearchToggle.tsx similarity index 100% rename from all-model-chat/components/chat/input/actions/WebSearchToggle.tsx rename to all-model-chat/apps/web/components/chat/input/actions/WebSearchToggle.tsx diff --git a/all-model-chat/components/chat/input/area/ChatFilePreviewList.tsx b/all-model-chat/apps/web/components/chat/input/area/ChatFilePreviewList.tsx similarity index 100% rename from all-model-chat/components/chat/input/area/ChatFilePreviewList.tsx rename to all-model-chat/apps/web/components/chat/input/area/ChatFilePreviewList.tsx diff --git a/all-model-chat/components/chat/input/area/ChatQuoteDisplay.tsx b/all-model-chat/apps/web/components/chat/input/area/ChatQuoteDisplay.tsx similarity index 100% rename from all-model-chat/components/chat/input/area/ChatQuoteDisplay.tsx rename to all-model-chat/apps/web/components/chat/input/area/ChatQuoteDisplay.tsx diff --git a/all-model-chat/components/chat/input/area/ChatSuggestions.tsx b/all-model-chat/apps/web/components/chat/input/area/ChatSuggestions.tsx similarity index 100% rename from all-model-chat/components/chat/input/area/ChatSuggestions.tsx rename to all-model-chat/apps/web/components/chat/input/area/ChatSuggestions.tsx diff --git a/all-model-chat/components/chat/input/area/ChatTextArea.tsx b/all-model-chat/apps/web/components/chat/input/area/ChatTextArea.tsx similarity index 100% rename from all-model-chat/components/chat/input/area/ChatTextArea.tsx rename to all-model-chat/apps/web/components/chat/input/area/ChatTextArea.tsx diff --git a/all-model-chat/components/chat/input/area/SuggestionIcon.tsx b/all-model-chat/apps/web/components/chat/input/area/SuggestionIcon.tsx similarity index 100% rename from all-model-chat/components/chat/input/area/SuggestionIcon.tsx rename to all-model-chat/apps/web/components/chat/input/area/SuggestionIcon.tsx diff --git a/all-model-chat/components/chat/input/toolbar/AddFileByIdInput.tsx b/all-model-chat/apps/web/components/chat/input/toolbar/AddFileByIdInput.tsx similarity index 100% rename from all-model-chat/components/chat/input/toolbar/AddFileByIdInput.tsx rename to all-model-chat/apps/web/components/chat/input/toolbar/AddFileByIdInput.tsx diff --git a/all-model-chat/components/chat/input/toolbar/AddUrlInput.tsx b/all-model-chat/apps/web/components/chat/input/toolbar/AddUrlInput.tsx similarity index 100% rename from all-model-chat/components/chat/input/toolbar/AddUrlInput.tsx rename to all-model-chat/apps/web/components/chat/input/toolbar/AddUrlInput.tsx diff --git a/all-model-chat/components/chat/input/toolbar/ImageSizeSelector.tsx b/all-model-chat/apps/web/components/chat/input/toolbar/ImageSizeSelector.tsx similarity index 100% rename from all-model-chat/components/chat/input/toolbar/ImageSizeSelector.tsx rename to all-model-chat/apps/web/components/chat/input/toolbar/ImageSizeSelector.tsx diff --git a/all-model-chat/components/chat/input/toolbar/ImagenAspectRatioSelector.tsx b/all-model-chat/apps/web/components/chat/input/toolbar/ImagenAspectRatioSelector.tsx similarity index 100% rename from all-model-chat/components/chat/input/toolbar/ImagenAspectRatioSelector.tsx rename to all-model-chat/apps/web/components/chat/input/toolbar/ImagenAspectRatioSelector.tsx diff --git a/all-model-chat/components/chat/input/toolbar/InputBar.tsx b/all-model-chat/apps/web/components/chat/input/toolbar/InputBar.tsx similarity index 100% rename from all-model-chat/components/chat/input/toolbar/InputBar.tsx rename to all-model-chat/apps/web/components/chat/input/toolbar/InputBar.tsx diff --git a/all-model-chat/components/chat/input/toolbar/MediaResolutionSelector.tsx b/all-model-chat/apps/web/components/chat/input/toolbar/MediaResolutionSelector.tsx similarity index 100% rename from all-model-chat/components/chat/input/toolbar/MediaResolutionSelector.tsx rename to all-model-chat/apps/web/components/chat/input/toolbar/MediaResolutionSelector.tsx diff --git a/all-model-chat/components/chat/input/toolbar/QuadImageToggle.tsx b/all-model-chat/apps/web/components/chat/input/toolbar/QuadImageToggle.tsx similarity index 100% rename from all-model-chat/components/chat/input/toolbar/QuadImageToggle.tsx rename to all-model-chat/apps/web/components/chat/input/toolbar/QuadImageToggle.tsx diff --git a/all-model-chat/components/chat/input/toolbar/TtsVoiceSelector.tsx b/all-model-chat/apps/web/components/chat/input/toolbar/TtsVoiceSelector.tsx similarity index 100% rename from all-model-chat/components/chat/input/toolbar/TtsVoiceSelector.tsx rename to all-model-chat/apps/web/components/chat/input/toolbar/TtsVoiceSelector.tsx diff --git a/all-model-chat/components/chat/message-list/MessageListPlaceholder.tsx b/all-model-chat/apps/web/components/chat/message-list/MessageListPlaceholder.tsx similarity index 100% rename from all-model-chat/components/chat/message-list/MessageListPlaceholder.tsx rename to all-model-chat/apps/web/components/chat/message-list/MessageListPlaceholder.tsx diff --git a/all-model-chat/components/chat/message-list/ScrollNavigation.tsx b/all-model-chat/apps/web/components/chat/message-list/ScrollNavigation.tsx similarity index 100% rename from all-model-chat/components/chat/message-list/ScrollNavigation.tsx rename to all-model-chat/apps/web/components/chat/message-list/ScrollNavigation.tsx diff --git a/all-model-chat/components/chat/message-list/TextSelectionToolbar.tsx b/all-model-chat/apps/web/components/chat/message-list/TextSelectionToolbar.tsx similarity index 100% rename from all-model-chat/components/chat/message-list/TextSelectionToolbar.tsx rename to all-model-chat/apps/web/components/chat/message-list/TextSelectionToolbar.tsx diff --git a/all-model-chat/components/chat/message-list/WelcomeScreen.tsx b/all-model-chat/apps/web/components/chat/message-list/WelcomeScreen.tsx similarity index 100% rename from all-model-chat/components/chat/message-list/WelcomeScreen.tsx rename to all-model-chat/apps/web/components/chat/message-list/WelcomeScreen.tsx diff --git a/all-model-chat/components/chat/message-list/text-selection/AudioPlayerView.tsx b/all-model-chat/apps/web/components/chat/message-list/text-selection/AudioPlayerView.tsx similarity index 100% rename from all-model-chat/components/chat/message-list/text-selection/AudioPlayerView.tsx rename to all-model-chat/apps/web/components/chat/message-list/text-selection/AudioPlayerView.tsx diff --git a/all-model-chat/components/chat/message-list/text-selection/StandardActionsView.tsx b/all-model-chat/apps/web/components/chat/message-list/text-selection/StandardActionsView.tsx similarity index 100% rename from all-model-chat/components/chat/message-list/text-selection/StandardActionsView.tsx rename to all-model-chat/apps/web/components/chat/message-list/text-selection/StandardActionsView.tsx diff --git a/all-model-chat/components/chat/message-list/text-selection/ToolbarContainer.tsx b/all-model-chat/apps/web/components/chat/message-list/text-selection/ToolbarContainer.tsx similarity index 100% rename from all-model-chat/components/chat/message-list/text-selection/ToolbarContainer.tsx rename to all-model-chat/apps/web/components/chat/message-list/text-selection/ToolbarContainer.tsx diff --git a/all-model-chat/components/chat/overlays/DragDropOverlay.tsx b/all-model-chat/apps/web/components/chat/overlays/DragDropOverlay.tsx similarity index 100% rename from all-model-chat/components/chat/overlays/DragDropOverlay.tsx rename to all-model-chat/apps/web/components/chat/overlays/DragDropOverlay.tsx diff --git a/all-model-chat/components/chat/overlays/ModelsErrorDisplay.tsx b/all-model-chat/apps/web/components/chat/overlays/ModelsErrorDisplay.tsx similarity index 100% rename from all-model-chat/components/chat/overlays/ModelsErrorDisplay.tsx rename to all-model-chat/apps/web/components/chat/overlays/ModelsErrorDisplay.tsx diff --git a/all-model-chat/components/header/Header.tsx b/all-model-chat/apps/web/components/header/Header.tsx similarity index 100% rename from all-model-chat/components/header/Header.tsx rename to all-model-chat/apps/web/components/header/Header.tsx diff --git a/all-model-chat/components/header/HeaderModelSelector.tsx b/all-model-chat/apps/web/components/header/HeaderModelSelector.tsx similarity index 100% rename from all-model-chat/components/header/HeaderModelSelector.tsx rename to all-model-chat/apps/web/components/header/HeaderModelSelector.tsx diff --git a/all-model-chat/components/icons/AppLogo.tsx b/all-model-chat/apps/web/components/icons/AppLogo.tsx similarity index 100% rename from all-model-chat/components/icons/AppLogo.tsx rename to all-model-chat/apps/web/components/icons/AppLogo.tsx diff --git a/all-model-chat/components/icons/CommandIcon.tsx b/all-model-chat/apps/web/components/icons/CommandIcon.tsx similarity index 100% rename from all-model-chat/components/icons/CommandIcon.tsx rename to all-model-chat/apps/web/components/icons/CommandIcon.tsx diff --git a/all-model-chat/components/icons/CustomIcons.tsx b/all-model-chat/apps/web/components/icons/CustomIcons.tsx similarity index 100% rename from all-model-chat/components/icons/CustomIcons.tsx rename to all-model-chat/apps/web/components/icons/CustomIcons.tsx diff --git a/all-model-chat/components/icons/GoogleSpinner.tsx b/all-model-chat/apps/web/components/icons/GoogleSpinner.tsx similarity index 100% rename from all-model-chat/components/icons/GoogleSpinner.tsx rename to all-model-chat/apps/web/components/icons/GoogleSpinner.tsx diff --git a/all-model-chat/components/icons/groups/AttachmentIcons.tsx b/all-model-chat/apps/web/components/icons/groups/AttachmentIcons.tsx similarity index 100% rename from all-model-chat/components/icons/groups/AttachmentIcons.tsx rename to all-model-chat/apps/web/components/icons/groups/AttachmentIcons.tsx diff --git a/all-model-chat/components/icons/groups/GeneralIcons.tsx b/all-model-chat/apps/web/components/icons/groups/GeneralIcons.tsx similarity index 100% rename from all-model-chat/components/icons/groups/GeneralIcons.tsx rename to all-model-chat/apps/web/components/icons/groups/GeneralIcons.tsx diff --git a/all-model-chat/components/icons/groups/SettingsIcons.tsx b/all-model-chat/apps/web/components/icons/groups/SettingsIcons.tsx similarity index 100% rename from all-model-chat/components/icons/groups/SettingsIcons.tsx rename to all-model-chat/apps/web/components/icons/groups/SettingsIcons.tsx diff --git a/all-model-chat/components/icons/groups/ThemeIcons.tsx b/all-model-chat/apps/web/components/icons/groups/ThemeIcons.tsx similarity index 100% rename from all-model-chat/components/icons/groups/ThemeIcons.tsx rename to all-model-chat/apps/web/components/icons/groups/ThemeIcons.tsx diff --git a/all-model-chat/components/icons/iconUtils.ts b/all-model-chat/apps/web/components/icons/iconUtils.ts similarity index 100% rename from all-model-chat/components/icons/iconUtils.ts rename to all-model-chat/apps/web/components/icons/iconUtils.ts diff --git a/all-model-chat/components/layout/ChatArea.tsx b/all-model-chat/apps/web/components/layout/ChatArea.tsx similarity index 98% rename from all-model-chat/components/layout/ChatArea.tsx rename to all-model-chat/apps/web/components/layout/ChatArea.tsx index 1eaa2475..7d86094e 100644 --- a/all-model-chat/components/layout/ChatArea.tsx +++ b/all-model-chat/apps/web/components/layout/ChatArea.tsx @@ -29,7 +29,7 @@ export const ChatArea: React.FC = (props) => { onScrollToPrevTurn, onScrollToNextTurn, onEditMessageContent, onUpdateMessageFile, appSettings, commandedInput, setCommandedInput, onMessageSent, selectedFiles, setSelectedFiles, onSendMessage, isEditing, editMode, editingMessageId, setEditingMessageId, onStopGenerating, - onCancelEdit, onProcessFiles, onAddFileById, onCancelUpload, onTranscribeAudio, + onCancelEdit, onProcessFiles, onProjectContextCreated, onAddFileById, onCancelUpload, onTranscribeAudio, isProcessingFile, fileError, isImageEditModel, aspectRatio, setAspectRatio, imageSize, setImageSize, isGoogleSearchEnabled, onToggleGoogleSearch, isCodeExecutionEnabled, onToggleCodeExecution, isUrlContextEnabled, onToggleUrlContext, isDeepSearchEnabled, onToggleDeepSearch, @@ -144,6 +144,7 @@ export const ChatArea: React.FC = (props) => { onStopGenerating={onStopGenerating} onCancelEdit={onCancelEdit} onProcessFiles={onProcessFiles} + onProjectContextCreated={onProjectContextCreated} onAddFileById={onAddFileById} onCancelUpload={onCancelUpload} onTranscribeAudio={onTranscribeAudio} diff --git a/all-model-chat/components/layout/MainContent.tsx b/all-model-chat/apps/web/components/layout/MainContent.tsx similarity index 56% rename from all-model-chat/components/layout/MainContent.tsx rename to all-model-chat/apps/web/components/layout/MainContent.tsx index acca56a5..715b682c 100644 --- a/all-model-chat/components/layout/MainContent.tsx +++ b/all-model-chat/apps/web/components/layout/MainContent.tsx @@ -4,11 +4,16 @@ import { HistorySidebar, HistorySidebarProps } from '../sidebar/HistorySidebar'; import { ChatArea, ChatAreaProps } from './ChatArea'; import { AppModals, AppModalsProps } from '../modals/AppModals'; import { SidePanel } from './SidePanel'; -import { SideViewContent } from '../../types'; +import { FolderExplorer } from '../sidebar/FolderExplorer'; +import { SideViewContent, ProjectContext, ProjectContextReadState } from '../../types'; interface MainContentProps { sidebarProps: HistorySidebarProps; - chatAreaProps: ChatAreaProps; + chatAreaProps: ChatAreaProps & { + projectContext?: ProjectContext | null; + projectContextReadState?: ProjectContextReadState; + onClearProjectContext?: () => void; + }; appModalsProps: AppModalsProps; isHistorySidebarOpen: boolean; setIsHistorySidebarOpen: (isOpen: boolean | ((prev: boolean) => boolean)) => void; @@ -27,26 +32,41 @@ export const MainContent: React.FC = ({ onCloseSidePanel, themeId, }) => { + const { projectContext, projectContextReadState, onClearProjectContext, t, ...restChatAreaProps } = chatAreaProps; + return ( <> {isHistorySidebarOpen && ( -
setIsHistorySidebarOpen(false)} +
setIsHistorySidebarOpen(false)} className="fixed inset-0 bg-black/60 z-40 transition-opacity duration-300 md:hidden" aria-hidden="true" /> )} - + + {/* Agentic folder explorer sidebar */} + {projectContext && onClearProjectContext && ( +
+ +
+ )} + {sidePanelContent && ( - )} - + ); diff --git a/all-model-chat/components/layout/PiPPlaceholder.tsx b/all-model-chat/apps/web/components/layout/PiPPlaceholder.tsx similarity index 100% rename from all-model-chat/components/layout/PiPPlaceholder.tsx rename to all-model-chat/apps/web/components/layout/PiPPlaceholder.tsx diff --git a/all-model-chat/components/layout/SidePanel.tsx b/all-model-chat/apps/web/components/layout/SidePanel.tsx similarity index 100% rename from all-model-chat/components/layout/SidePanel.tsx rename to all-model-chat/apps/web/components/layout/SidePanel.tsx diff --git a/all-model-chat/components/layout/chat-area/ChatAreaProps.ts b/all-model-chat/apps/web/components/layout/chat-area/ChatAreaProps.ts similarity index 96% rename from all-model-chat/components/layout/chat-area/ChatAreaProps.ts rename to all-model-chat/apps/web/components/layout/chat-area/ChatAreaProps.ts index af70985a..6ba150d9 100644 --- a/all-model-chat/components/layout/chat-area/ChatAreaProps.ts +++ b/all-model-chat/apps/web/components/layout/chat-area/ChatAreaProps.ts @@ -1,7 +1,7 @@ -import { ChatSettings, ChatMessage, UploadedFile, AppSettings, ModelOption, SideViewContent, VideoMetadata, InputCommand } from '../../../types'; +import { ChatSettings, ChatMessage, UploadedFile, AppSettings, ModelOption, SideViewContent, VideoMetadata, InputCommand, ProjectContext } from '../../../types'; import { translations } from '../../../utils/appUtils'; import { MediaResolution } from '../../../types/settings'; @@ -88,6 +88,7 @@ export interface ChatAreaProps { onStopGenerating: () => void; onCancelEdit: () => void; onProcessFiles: (files: FileList | File[]) => Promise; + onProjectContextCreated?: (context: ProjectContext) => void; onAddFileById: (fileId: string) => Promise; onCancelUpload: (fileId: string) => void; onTranscribeAudio: (file: File) => Promise; diff --git a/all-model-chat/components/layout/chat-area/useChatArea.ts b/all-model-chat/apps/web/components/layout/chat-area/useChatArea.ts similarity index 100% rename from all-model-chat/components/layout/chat-area/useChatArea.ts rename to all-model-chat/apps/web/components/layout/chat-area/useChatArea.ts diff --git a/all-model-chat/apps/web/components/log-viewer/ApiUsageTab.tsx b/all-model-chat/apps/web/components/log-viewer/ApiUsageTab.tsx new file mode 100644 index 00000000..75f6ab1e --- /dev/null +++ b/all-model-chat/apps/web/components/log-viewer/ApiUsageTab.tsx @@ -0,0 +1,67 @@ + +import React from 'react'; +import { KeyRound } from 'lucide-react'; + +interface ApiUsageTabProps { + apiKeyUsage: Map; +} + +const parseUsageKeyId = (usageKeyId: string): { source: string; keyId: string } => { + const separatorIndex = usageKeyId.indexOf(':'); + if (separatorIndex <= 0) { + return { source: 'unknown', keyId: usageKeyId }; + } + + return { + source: usageKeyId.slice(0, separatorIndex), + keyId: usageKeyId.slice(separatorIndex + 1), + }; +}; + +export const ApiUsageTab: React.FC = ({ apiKeyUsage }) => { + const entries = Array.from(apiKeyUsage.entries()) + .map(([usageKeyId, count]) => ({ usageKeyId, count, ...parseUsageKeyId(usageKeyId) })) + .sort((a, b) => b.count - a.count); + const totalApiUsage = entries.reduce((sum, entry) => sum + entry.count, 0); + + return ( +
+

Provider Key Usage Statistics

+ {entries.length === 0 && ( +

+ No provider key usage has been recorded yet. +

+ )} +
+ {entries.map(({ usageKeyId, keyId, source, count }, index) => { + const percentage = totalApiUsage > 0 ? (count / totalApiUsage) * 100 : 0; + return ( +
+
+ #{index + 1} + + {source} + +
+
+ + {keyId} + +
+
+
+ {count} + requests +
+
+ {percentage.toFixed(0)}% +
+
+
+
+ ); + })} +
+
+ ); +}; diff --git a/all-model-chat/components/log-viewer/ConsoleTab.tsx b/all-model-chat/apps/web/components/log-viewer/ConsoleTab.tsx similarity index 100% rename from all-model-chat/components/log-viewer/ConsoleTab.tsx rename to all-model-chat/apps/web/components/log-viewer/ConsoleTab.tsx diff --git a/all-model-chat/components/log-viewer/LogRow.tsx b/all-model-chat/apps/web/components/log-viewer/LogRow.tsx similarity index 100% rename from all-model-chat/components/log-viewer/LogRow.tsx rename to all-model-chat/apps/web/components/log-viewer/LogRow.tsx diff --git a/all-model-chat/components/log-viewer/LogViewer.tsx b/all-model-chat/apps/web/components/log-viewer/LogViewer.tsx similarity index 87% rename from all-model-chat/components/log-viewer/LogViewer.tsx rename to all-model-chat/apps/web/components/log-viewer/LogViewer.tsx index b92b6bac..dda5b18d 100644 --- a/all-model-chat/components/log-viewer/LogViewer.tsx +++ b/all-model-chat/apps/web/components/log-viewer/LogViewer.tsx @@ -64,11 +64,10 @@ export const LogViewer: React.FC = ({ isOpen, onClose, appSettin }, [isOpen]); useEffect(() => { - if (isOpen && appSettings.useCustomApiConfig) { - const unsubscribe = logService.subscribeToApiKeys(setApiKeyUsage); - return () => unsubscribe(); - } - }, [isOpen, appSettings.useCustomApiConfig]); + if (!isOpen) return; + const unsubscribe = logService.subscribeToApiKeys(setApiKeyUsage); + return () => unsubscribe(); + }, [isOpen]); useEffect(() => { if (isOpen) { @@ -110,11 +109,9 @@ export const LogViewer: React.FC = ({ isOpen, onClose, appSettin - {appSettings.useCustomApiConfig && ( - - )} +
@@ -135,7 +132,7 @@ export const LogViewer: React.FC = ({ isOpen, onClose, appSettin )} {activeTab === 'api' && ( - + )}
diff --git a/all-model-chat/components/log-viewer/ObfuscatedApiKey.tsx b/all-model-chat/apps/web/components/log-viewer/ObfuscatedApiKey.tsx similarity index 100% rename from all-model-chat/components/log-viewer/ObfuscatedApiKey.tsx rename to all-model-chat/apps/web/components/log-viewer/ObfuscatedApiKey.tsx diff --git a/all-model-chat/components/log-viewer/TokenUsageTab.tsx b/all-model-chat/apps/web/components/log-viewer/TokenUsageTab.tsx similarity index 100% rename from all-model-chat/components/log-viewer/TokenUsageTab.tsx rename to all-model-chat/apps/web/components/log-viewer/TokenUsageTab.tsx diff --git a/all-model-chat/components/log-viewer/constants.ts b/all-model-chat/apps/web/components/log-viewer/constants.ts similarity index 100% rename from all-model-chat/components/log-viewer/constants.ts rename to all-model-chat/apps/web/components/log-viewer/constants.ts diff --git a/all-model-chat/components/message/FileDisplay.tsx b/all-model-chat/apps/web/components/message/FileDisplay.tsx similarity index 100% rename from all-model-chat/components/message/FileDisplay.tsx rename to all-model-chat/apps/web/components/message/FileDisplay.tsx diff --git a/all-model-chat/components/message/GroundedResponse.tsx b/all-model-chat/apps/web/components/message/GroundedResponse.tsx similarity index 100% rename from all-model-chat/components/message/GroundedResponse.tsx rename to all-model-chat/apps/web/components/message/GroundedResponse.tsx diff --git a/all-model-chat/components/message/MarkdownRenderer.tsx b/all-model-chat/apps/web/components/message/MarkdownRenderer.tsx similarity index 100% rename from all-model-chat/components/message/MarkdownRenderer.tsx rename to all-model-chat/apps/web/components/message/MarkdownRenderer.tsx diff --git a/all-model-chat/components/message/Message.tsx b/all-model-chat/apps/web/components/message/Message.tsx similarity index 100% rename from all-model-chat/components/message/Message.tsx rename to all-model-chat/apps/web/components/message/Message.tsx diff --git a/all-model-chat/components/message/MessageActions.tsx b/all-model-chat/apps/web/components/message/MessageActions.tsx similarity index 100% rename from all-model-chat/components/message/MessageActions.tsx rename to all-model-chat/apps/web/components/message/MessageActions.tsx diff --git a/all-model-chat/components/message/MessageContent.tsx b/all-model-chat/apps/web/components/message/MessageContent.tsx similarity index 100% rename from all-model-chat/components/message/MessageContent.tsx rename to all-model-chat/apps/web/components/message/MessageContent.tsx diff --git a/all-model-chat/components/message/PerformanceMetrics.tsx b/all-model-chat/apps/web/components/message/PerformanceMetrics.tsx similarity index 100% rename from all-model-chat/components/message/PerformanceMetrics.tsx rename to all-model-chat/apps/web/components/message/PerformanceMetrics.tsx diff --git a/all-model-chat/components/message/ThinkingTimer.tsx b/all-model-chat/apps/web/components/message/ThinkingTimer.tsx similarity index 100% rename from all-model-chat/components/message/ThinkingTimer.tsx rename to all-model-chat/apps/web/components/message/ThinkingTimer.tsx diff --git a/all-model-chat/components/message/blocks/CodeBlock.tsx b/all-model-chat/apps/web/components/message/blocks/CodeBlock.tsx similarity index 100% rename from all-model-chat/components/message/blocks/CodeBlock.tsx rename to all-model-chat/apps/web/components/message/blocks/CodeBlock.tsx diff --git a/all-model-chat/components/message/blocks/GraphvizBlock.tsx b/all-model-chat/apps/web/components/message/blocks/GraphvizBlock.tsx similarity index 100% rename from all-model-chat/components/message/blocks/GraphvizBlock.tsx rename to all-model-chat/apps/web/components/message/blocks/GraphvizBlock.tsx diff --git a/all-model-chat/apps/web/components/message/blocks/MermaidBlock.tsx b/all-model-chat/apps/web/components/message/blocks/MermaidBlock.tsx new file mode 100644 index 00000000..b9957625 --- /dev/null +++ b/all-model-chat/apps/web/components/message/blocks/MermaidBlock.tsx @@ -0,0 +1,208 @@ + +import React, { useEffect, useState, useRef } from 'react'; +import { Loader2, AlertTriangle, Download, Maximize, Code, Copy, Check, Sidebar } from 'lucide-react'; +import { SideViewContent, UploadedFile } from '../../../types'; +import { exportSvgAsImage } from '../../../utils/exportUtils'; +import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'; +import { MESSAGE_BLOCK_BUTTON_CLASS } from '../../../constants/appConstants'; + +// Module-level cache for the dynamically imported mermaid library +let mermaidModule: typeof import('mermaid') | null = null; +let mermaidLoadPromise: Promise | null = null; + +const getMermaid = async () => { + if (mermaidModule) return mermaidModule; + if (!mermaidLoadPromise) { + mermaidLoadPromise = import('mermaid'); + } + mermaidModule = await mermaidLoadPromise; + return mermaidModule; +}; + +interface MermaidBlockProps { + code: string; + onImageClick: (file: UploadedFile) => void; + isLoading: boolean; + themeId: string; + onOpenSidePanel: (content: SideViewContent) => void; +} + +export const MermaidBlock: React.FC = ({ code, onImageClick, isLoading: isMessageLoading, themeId, onOpenSidePanel }) => { + const [svg, setSvg] = useState(''); + const [error, setError] = useState(''); + const [isRendering, setIsRendering] = useState(true); + const [isDownloading, setIsDownloading] = useState(false); + const [diagramFile, setDiagramFile] = useState(null); + const [showSource, setShowSource] = useState(false); + const { isCopied, copyToClipboard } = useCopyToClipboard(); + const diagramContainerRef = useRef(null); + + useEffect(() => { + let isMounted = true; + + // Debounce rendering to avoid syntax errors while typing + const timeoutId = setTimeout(async () => { + if (!code) return; + + try { + // Dynamically load mermaid (cached after first load) + const mermaidLib = await getMermaid(); + const mermaid = mermaidLib.default; + + if (!isMounted) return; + + const id = `mermaid-svg-${Math.random().toString(36).substring(2, 9)}`; + + mermaid.initialize({ + startOnLoad: false, + theme: themeId === 'onyx' ? 'dark' : 'default', + securityLevel: 'loose', + fontFamily: 'inherit' + }); + + const { svg: renderedSvg } = await mermaid.render(id, code); + + if (!isMounted) return; + + setSvg(renderedSvg); + + const svgDataUrl = `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(renderedSvg)))}`; + setDiagramFile({ + id: id, + name: 'mermaid-diagram.svg', + type: 'image/svg+xml', + size: renderedSvg.length, + dataUrl: svgDataUrl, + uploadState: 'active' + }); + setError(''); + setIsRendering(false); + + } catch (e) { + if (!isMounted) return; + + if (isMessageLoading) { + // If still loading (streaming), treat parsing errors as "generating..." + // Show spinner to indicate incomplete state + setIsRendering(true); + } else { + // Final error state + const errorMessage = e instanceof Error ? e.message : 'Failed to render Mermaid diagram.'; + setError(errorMessage.replace(/.*error:\s*/, '')); + setSvg(''); + setIsRendering(false); + } + } + }, 500); // 500ms debounce + + return () => { + isMounted = false; + clearTimeout(timeoutId); + }; + }, [code, isMessageLoading, themeId]); + + const handleDownloadJpg = async () => { + if (!svg || isDownloading) return; + setIsDownloading(true); + try { + await exportSvgAsImage(svg, `mermaid-diagram-${Date.now()}.jpg`, 3, 'image/jpeg'); + } catch (e) { + const errorMessage = e instanceof Error ? e.message : 'Failed to export diagram as JPG.'; + setError(errorMessage); + } finally { + setIsDownloading(false); + } + }; + + const handleCopyCode = () => { + copyToClipboard(code); + }; + + const containerClasses = "p-4 border border-[var(--theme-border-secondary)] rounded-md shadow-inner overflow-auto custom-scrollbar flex items-center justify-center min-h-[150px] transition-colors duration-300"; + const bgClass = themeId === 'onyx' ? 'bg-[var(--theme-bg-secondary)]' : 'bg-white'; + + if (isRendering) { + return ( +
+ +
+ ); + } + + if (error) { + return ( +
+
+
+ + Mermaid Error +
{error}
+
+
+ {/* Always show code on error so user can debug */} +
+
{code}
+
+
+ ); + } + + return ( +
+
+ Mermaid +
+ + + {diagramFile && ( + <> + + + + )} +
+
+ +
diagramFile && onImageClick(diagramFile)} + dangerouslySetInnerHTML={{ __html: svg }} + /> + + {showSource && ( +
+
+ +
+
+            {code}
+          
+
+ )} +
+ ); +}; diff --git a/all-model-chat/components/message/blocks/TableBlock.tsx b/all-model-chat/apps/web/components/message/blocks/TableBlock.tsx similarity index 100% rename from all-model-chat/components/message/blocks/TableBlock.tsx rename to all-model-chat/apps/web/components/message/blocks/TableBlock.tsx diff --git a/all-model-chat/components/message/blocks/ToolResultBlock.tsx b/all-model-chat/apps/web/components/message/blocks/ToolResultBlock.tsx similarity index 100% rename from all-model-chat/components/message/blocks/ToolResultBlock.tsx rename to all-model-chat/apps/web/components/message/blocks/ToolResultBlock.tsx diff --git a/all-model-chat/components/message/blocks/parts/CodeHeader.tsx b/all-model-chat/apps/web/components/message/blocks/parts/CodeHeader.tsx similarity index 100% rename from all-model-chat/components/message/blocks/parts/CodeHeader.tsx rename to all-model-chat/apps/web/components/message/blocks/parts/CodeHeader.tsx diff --git a/all-model-chat/components/message/buttons/ExportMessageButton.tsx b/all-model-chat/apps/web/components/message/buttons/ExportMessageButton.tsx similarity index 100% rename from all-model-chat/components/message/buttons/ExportMessageButton.tsx rename to all-model-chat/apps/web/components/message/buttons/ExportMessageButton.tsx diff --git a/all-model-chat/components/message/buttons/MessageCopyButton.tsx b/all-model-chat/apps/web/components/message/buttons/MessageCopyButton.tsx similarity index 100% rename from all-model-chat/components/message/buttons/MessageCopyButton.tsx rename to all-model-chat/apps/web/components/message/buttons/MessageCopyButton.tsx diff --git a/all-model-chat/components/message/buttons/export/ExportModal.tsx b/all-model-chat/apps/web/components/message/buttons/export/ExportModal.tsx similarity index 100% rename from all-model-chat/components/message/buttons/export/ExportModal.tsx rename to all-model-chat/apps/web/components/message/buttons/export/ExportModal.tsx diff --git a/all-model-chat/components/message/buttons/export/ExportOptions.tsx b/all-model-chat/apps/web/components/message/buttons/export/ExportOptions.tsx similarity index 100% rename from all-model-chat/components/message/buttons/export/ExportOptions.tsx rename to all-model-chat/apps/web/components/message/buttons/export/ExportOptions.tsx diff --git a/all-model-chat/components/message/code-block/InlineCode.tsx b/all-model-chat/apps/web/components/message/code-block/InlineCode.tsx similarity index 100% rename from all-model-chat/components/message/code-block/InlineCode.tsx rename to all-model-chat/apps/web/components/message/code-block/InlineCode.tsx diff --git a/all-model-chat/components/message/code-block/LanguageIcon.tsx b/all-model-chat/apps/web/components/message/code-block/LanguageIcon.tsx similarity index 100% rename from all-model-chat/components/message/code-block/LanguageIcon.tsx rename to all-model-chat/apps/web/components/message/code-block/LanguageIcon.tsx diff --git a/all-model-chat/components/message/content/MessageFiles.tsx b/all-model-chat/apps/web/components/message/content/MessageFiles.tsx similarity index 100% rename from all-model-chat/components/message/content/MessageFiles.tsx rename to all-model-chat/apps/web/components/message/content/MessageFiles.tsx diff --git a/all-model-chat/components/message/content/MessageFooter.tsx b/all-model-chat/apps/web/components/message/content/MessageFooter.tsx similarity index 100% rename from all-model-chat/components/message/content/MessageFooter.tsx rename to all-model-chat/apps/web/components/message/content/MessageFooter.tsx diff --git a/all-model-chat/components/message/content/MessageText.tsx b/all-model-chat/apps/web/components/message/content/MessageText.tsx similarity index 100% rename from all-model-chat/components/message/content/MessageText.tsx rename to all-model-chat/apps/web/components/message/content/MessageText.tsx diff --git a/all-model-chat/components/message/content/MessageThoughts.tsx b/all-model-chat/apps/web/components/message/content/MessageThoughts.tsx similarity index 100% rename from all-model-chat/components/message/content/MessageThoughts.tsx rename to all-model-chat/apps/web/components/message/content/MessageThoughts.tsx diff --git a/all-model-chat/components/message/content/thoughts/ThinkingActions.tsx b/all-model-chat/apps/web/components/message/content/thoughts/ThinkingActions.tsx similarity index 100% rename from all-model-chat/components/message/content/thoughts/ThinkingActions.tsx rename to all-model-chat/apps/web/components/message/content/thoughts/ThinkingActions.tsx diff --git a/all-model-chat/components/message/content/thoughts/ThinkingHeader.tsx b/all-model-chat/apps/web/components/message/content/thoughts/ThinkingHeader.tsx similarity index 100% rename from all-model-chat/components/message/content/thoughts/ThinkingHeader.tsx rename to all-model-chat/apps/web/components/message/content/thoughts/ThinkingHeader.tsx diff --git a/all-model-chat/components/message/content/thoughts/ThoughtContent.tsx b/all-model-chat/apps/web/components/message/content/thoughts/ThoughtContent.tsx similarity index 100% rename from all-model-chat/components/message/content/thoughts/ThoughtContent.tsx rename to all-model-chat/apps/web/components/message/content/thoughts/ThoughtContent.tsx diff --git a/all-model-chat/components/message/grounded-response/ContextUrls.tsx b/all-model-chat/apps/web/components/message/grounded-response/ContextUrls.tsx similarity index 100% rename from all-model-chat/components/message/grounded-response/ContextUrls.tsx rename to all-model-chat/apps/web/components/message/grounded-response/ContextUrls.tsx diff --git a/all-model-chat/components/message/grounded-response/SearchQueries.tsx b/all-model-chat/apps/web/components/message/grounded-response/SearchQueries.tsx similarity index 100% rename from all-model-chat/components/message/grounded-response/SearchQueries.tsx rename to all-model-chat/apps/web/components/message/grounded-response/SearchQueries.tsx diff --git a/all-model-chat/components/message/grounded-response/SearchSources.tsx b/all-model-chat/apps/web/components/message/grounded-response/SearchSources.tsx similarity index 100% rename from all-model-chat/components/message/grounded-response/SearchSources.tsx rename to all-model-chat/apps/web/components/message/grounded-response/SearchSources.tsx diff --git a/all-model-chat/components/message/grounded-response/utils.ts b/all-model-chat/apps/web/components/message/grounded-response/utils.ts similarity index 100% rename from all-model-chat/components/message/grounded-response/utils.ts rename to all-model-chat/apps/web/components/message/grounded-response/utils.ts diff --git a/all-model-chat/components/modals/AppModals.tsx b/all-model-chat/apps/web/components/modals/AppModals.tsx similarity index 100% rename from all-model-chat/components/modals/AppModals.tsx rename to all-model-chat/apps/web/components/modals/AppModals.tsx diff --git a/all-model-chat/components/modals/AudioRecorder.tsx b/all-model-chat/apps/web/components/modals/AudioRecorder.tsx similarity index 100% rename from all-model-chat/components/modals/AudioRecorder.tsx rename to all-model-chat/apps/web/components/modals/AudioRecorder.tsx diff --git a/all-model-chat/components/modals/ConfirmationModal.tsx b/all-model-chat/apps/web/components/modals/ConfirmationModal.tsx similarity index 100% rename from all-model-chat/components/modals/ConfirmationModal.tsx rename to all-model-chat/apps/web/components/modals/ConfirmationModal.tsx diff --git a/all-model-chat/components/modals/CreateTextFileEditor.tsx b/all-model-chat/apps/web/components/modals/CreateTextFileEditor.tsx similarity index 100% rename from all-model-chat/components/modals/CreateTextFileEditor.tsx rename to all-model-chat/apps/web/components/modals/CreateTextFileEditor.tsx diff --git a/all-model-chat/components/modals/ExportChatModal.tsx b/all-model-chat/apps/web/components/modals/ExportChatModal.tsx similarity index 100% rename from all-model-chat/components/modals/ExportChatModal.tsx rename to all-model-chat/apps/web/components/modals/ExportChatModal.tsx diff --git a/all-model-chat/components/modals/FileConfigurationModal.tsx b/all-model-chat/apps/web/components/modals/FileConfigurationModal.tsx similarity index 100% rename from all-model-chat/components/modals/FileConfigurationModal.tsx rename to all-model-chat/apps/web/components/modals/FileConfigurationModal.tsx diff --git a/all-model-chat/components/modals/FilePreviewModal.tsx b/all-model-chat/apps/web/components/modals/FilePreviewModal.tsx similarity index 100% rename from all-model-chat/components/modals/FilePreviewModal.tsx rename to all-model-chat/apps/web/components/modals/FilePreviewModal.tsx diff --git a/all-model-chat/components/modals/HelpModal.tsx b/all-model-chat/apps/web/components/modals/HelpModal.tsx similarity index 100% rename from all-model-chat/components/modals/HelpModal.tsx rename to all-model-chat/apps/web/components/modals/HelpModal.tsx diff --git a/all-model-chat/components/modals/HtmlPreviewModal.tsx b/all-model-chat/apps/web/components/modals/HtmlPreviewModal.tsx similarity index 100% rename from all-model-chat/components/modals/HtmlPreviewModal.tsx rename to all-model-chat/apps/web/components/modals/HtmlPreviewModal.tsx diff --git a/all-model-chat/components/modals/TextEditorModal.tsx b/all-model-chat/apps/web/components/modals/TextEditorModal.tsx similarity index 100% rename from all-model-chat/components/modals/TextEditorModal.tsx rename to all-model-chat/apps/web/components/modals/TextEditorModal.tsx diff --git a/all-model-chat/components/modals/TokenCountModal.tsx b/all-model-chat/apps/web/components/modals/TokenCountModal.tsx similarity index 100% rename from all-model-chat/components/modals/TokenCountModal.tsx rename to all-model-chat/apps/web/components/modals/TokenCountModal.tsx diff --git a/all-model-chat/components/modals/create-file/CreateFileBody.tsx b/all-model-chat/apps/web/components/modals/create-file/CreateFileBody.tsx similarity index 100% rename from all-model-chat/components/modals/create-file/CreateFileBody.tsx rename to all-model-chat/apps/web/components/modals/create-file/CreateFileBody.tsx diff --git a/all-model-chat/components/modals/create-file/CreateFileFooter.tsx b/all-model-chat/apps/web/components/modals/create-file/CreateFileFooter.tsx similarity index 100% rename from all-model-chat/components/modals/create-file/CreateFileFooter.tsx rename to all-model-chat/apps/web/components/modals/create-file/CreateFileFooter.tsx diff --git a/all-model-chat/components/modals/create-file/CreateFileHeader.tsx b/all-model-chat/apps/web/components/modals/create-file/CreateFileHeader.tsx similarity index 100% rename from all-model-chat/components/modals/create-file/CreateFileHeader.tsx rename to all-model-chat/apps/web/components/modals/create-file/CreateFileHeader.tsx diff --git a/all-model-chat/components/modals/file-config/FileConfigFooter.tsx b/all-model-chat/apps/web/components/modals/file-config/FileConfigFooter.tsx similarity index 100% rename from all-model-chat/components/modals/file-config/FileConfigFooter.tsx rename to all-model-chat/apps/web/components/modals/file-config/FileConfigFooter.tsx diff --git a/all-model-chat/components/modals/file-config/FileConfigHeader.tsx b/all-model-chat/apps/web/components/modals/file-config/FileConfigHeader.tsx similarity index 100% rename from all-model-chat/components/modals/file-config/FileConfigHeader.tsx rename to all-model-chat/apps/web/components/modals/file-config/FileConfigHeader.tsx diff --git a/all-model-chat/components/modals/file-config/ResolutionConfig.tsx b/all-model-chat/apps/web/components/modals/file-config/ResolutionConfig.tsx similarity index 100% rename from all-model-chat/components/modals/file-config/ResolutionConfig.tsx rename to all-model-chat/apps/web/components/modals/file-config/ResolutionConfig.tsx diff --git a/all-model-chat/components/modals/file-config/VideoConfig.tsx b/all-model-chat/apps/web/components/modals/file-config/VideoConfig.tsx similarity index 100% rename from all-model-chat/components/modals/file-config/VideoConfig.tsx rename to all-model-chat/apps/web/components/modals/file-config/VideoConfig.tsx diff --git a/all-model-chat/components/modals/html-preview/HtmlPreviewContent.tsx b/all-model-chat/apps/web/components/modals/html-preview/HtmlPreviewContent.tsx similarity index 100% rename from all-model-chat/components/modals/html-preview/HtmlPreviewContent.tsx rename to all-model-chat/apps/web/components/modals/html-preview/HtmlPreviewContent.tsx diff --git a/all-model-chat/components/modals/html-preview/HtmlPreviewHeader.tsx b/all-model-chat/apps/web/components/modals/html-preview/HtmlPreviewHeader.tsx similarity index 100% rename from all-model-chat/components/modals/html-preview/HtmlPreviewHeader.tsx rename to all-model-chat/apps/web/components/modals/html-preview/HtmlPreviewHeader.tsx diff --git a/all-model-chat/components/modals/token-count/TokenCountFiles.tsx b/all-model-chat/apps/web/components/modals/token-count/TokenCountFiles.tsx similarity index 100% rename from all-model-chat/components/modals/token-count/TokenCountFiles.tsx rename to all-model-chat/apps/web/components/modals/token-count/TokenCountFiles.tsx diff --git a/all-model-chat/components/modals/token-count/TokenCountFooter.tsx b/all-model-chat/apps/web/components/modals/token-count/TokenCountFooter.tsx similarity index 100% rename from all-model-chat/components/modals/token-count/TokenCountFooter.tsx rename to all-model-chat/apps/web/components/modals/token-count/TokenCountFooter.tsx diff --git a/all-model-chat/components/modals/token-count/TokenCountInput.tsx b/all-model-chat/apps/web/components/modals/token-count/TokenCountInput.tsx similarity index 100% rename from all-model-chat/components/modals/token-count/TokenCountInput.tsx rename to all-model-chat/apps/web/components/modals/token-count/TokenCountInput.tsx diff --git a/all-model-chat/components/recorder/AudioVisualizer.tsx b/all-model-chat/apps/web/components/recorder/AudioVisualizer.tsx similarity index 100% rename from all-model-chat/components/recorder/AudioVisualizer.tsx rename to all-model-chat/apps/web/components/recorder/AudioVisualizer.tsx diff --git a/all-model-chat/components/recorder/RecorderControls.tsx b/all-model-chat/apps/web/components/recorder/RecorderControls.tsx similarity index 100% rename from all-model-chat/components/recorder/RecorderControls.tsx rename to all-model-chat/apps/web/components/recorder/RecorderControls.tsx diff --git a/all-model-chat/components/scenarios/PreloadedMessagesModal.tsx b/all-model-chat/apps/web/components/scenarios/PreloadedMessagesModal.tsx similarity index 100% rename from all-model-chat/components/scenarios/PreloadedMessagesModal.tsx rename to all-model-chat/apps/web/components/scenarios/PreloadedMessagesModal.tsx diff --git a/all-model-chat/components/scenarios/ScenarioEditor.tsx b/all-model-chat/apps/web/components/scenarios/ScenarioEditor.tsx similarity index 100% rename from all-model-chat/components/scenarios/ScenarioEditor.tsx rename to all-model-chat/apps/web/components/scenarios/ScenarioEditor.tsx diff --git a/all-model-chat/components/scenarios/ScenarioItem.tsx b/all-model-chat/apps/web/components/scenarios/ScenarioItem.tsx similarity index 100% rename from all-model-chat/components/scenarios/ScenarioItem.tsx rename to all-model-chat/apps/web/components/scenarios/ScenarioItem.tsx diff --git a/all-model-chat/components/scenarios/ScenarioList.tsx b/all-model-chat/apps/web/components/scenarios/ScenarioList.tsx similarity index 100% rename from all-model-chat/components/scenarios/ScenarioList.tsx rename to all-model-chat/apps/web/components/scenarios/ScenarioList.tsx diff --git a/all-model-chat/components/scenarios/editor/ScenarioEditorHeader.tsx b/all-model-chat/apps/web/components/scenarios/editor/ScenarioEditorHeader.tsx similarity index 100% rename from all-model-chat/components/scenarios/editor/ScenarioEditorHeader.tsx rename to all-model-chat/apps/web/components/scenarios/editor/ScenarioEditorHeader.tsx diff --git a/all-model-chat/components/scenarios/editor/ScenarioMessageInput.tsx b/all-model-chat/apps/web/components/scenarios/editor/ScenarioMessageInput.tsx similarity index 100% rename from all-model-chat/components/scenarios/editor/ScenarioMessageInput.tsx rename to all-model-chat/apps/web/components/scenarios/editor/ScenarioMessageInput.tsx diff --git a/all-model-chat/components/scenarios/editor/ScenarioMessageList.tsx b/all-model-chat/apps/web/components/scenarios/editor/ScenarioMessageList.tsx similarity index 100% rename from all-model-chat/components/scenarios/editor/ScenarioMessageList.tsx rename to all-model-chat/apps/web/components/scenarios/editor/ScenarioMessageList.tsx diff --git a/all-model-chat/components/scenarios/editor/ScenarioSystemPrompt.tsx b/all-model-chat/apps/web/components/scenarios/editor/ScenarioSystemPrompt.tsx similarity index 100% rename from all-model-chat/components/scenarios/editor/ScenarioSystemPrompt.tsx rename to all-model-chat/apps/web/components/scenarios/editor/ScenarioSystemPrompt.tsx diff --git a/all-model-chat/components/settings/ModelVoiceSettings.tsx b/all-model-chat/apps/web/components/settings/ModelVoiceSettings.tsx similarity index 100% rename from all-model-chat/components/settings/ModelVoiceSettings.tsx rename to all-model-chat/apps/web/components/settings/ModelVoiceSettings.tsx diff --git a/all-model-chat/components/settings/SettingsContent.tsx b/all-model-chat/apps/web/components/settings/SettingsContent.tsx similarity index 63% rename from all-model-chat/components/settings/SettingsContent.tsx rename to all-model-chat/apps/web/components/settings/SettingsContent.tsx index ee79dce7..602527f7 100644 --- a/all-model-chat/components/settings/SettingsContent.tsx +++ b/all-model-chat/apps/web/components/settings/SettingsContent.tsx @@ -9,15 +9,17 @@ import { ChatBehaviorSection } from './sections/ChatBehaviorSection'; import { DataManagementSection } from './sections/DataManagementSection'; import { ShortcutsSection } from './sections/ShortcutsSection'; import { AboutSection } from './sections/AboutSection'; +import { Save, X } from 'lucide-react'; interface SettingsContentProps { activeTab: SettingsTab; currentSettings: AppSettings; + pendingSettings: AppSettings; availableModels: ModelOption[]; updateSetting: (key: K, value: AppSettings[K]) => void; handleModelChange: (modelId: string) => void; setAvailableModels: (models: ModelOption[]) => void; - + // Data Management Handlers onClearHistory: () => void; onClearCache: () => void; @@ -32,13 +34,20 @@ interface SettingsContentProps { onExportHistory: () => void; onImportScenarios: (file: File) => void; onExportScenarios: () => void; - + + // Pending state handlers + hasUnsavedChanges: boolean; + onSave: () => void; + onDiscard: () => void; + requiresConfirmation: boolean; + t: (key: keyof typeof translations) => string; } export const SettingsContent: React.FC = ({ activeTab, currentSettings, + pendingSettings, availableModels, updateSetting, handleModelChange, @@ -56,6 +65,10 @@ export const SettingsContent: React.FC = ({ onExportHistory, onImportScenarios, onExportScenarios, + hasUnsavedChanges, + onSave, + onDiscard, + requiresConfirmation, t }) => { const animClass = "animate-in fade-in zoom-in-95 duration-200 ease-out"; @@ -66,57 +79,87 @@ export const SettingsContent: React.FC = ({ }); }; + // Use pending settings for tabs that require confirmation + const displaySettings = requiresConfirmation ? pendingSettings : currentSettings; + + // Action buttons for tabs that require confirmation + const ActionButtons = () => ( +
+
+ + +
+
+ ); + return (
{activeTab === 'model' && (
updateSetting('transcriptionModelId', v)} - generateQuadImages={currentSettings.generateQuadImages ?? false} setGenerateQuadImages={(v) => updateSetting('generateQuadImages', v)} - ttsVoice={currentSettings.ttsVoice} setTtsVoice={(v) => updateSetting('ttsVoice', v)} - systemInstruction={currentSettings.systemInstruction} setSystemInstruction={(v) => updateSetting('systemInstruction', v)} - temperature={currentSettings.temperature} setTemperature={(v) => updateSetting('temperature', v)} - topP={currentSettings.topP} setTopP={(v) => updateSetting('topP', v)} - showThoughts={currentSettings.showThoughts} setShowThoughts={(v) => updateSetting('showThoughts', v)} - thinkingBudget={currentSettings.thinkingBudget} setThinkingBudget={(v) => updateSetting('thinkingBudget', v)} - thinkingLevel={currentSettings.thinkingLevel} setThinkingLevel={(v) => updateSetting('thinkingLevel', v)} - safetySettings={currentSettings.safetySettings} setSafetySettings={(v) => updateSetting('safetySettings', v)} - mediaResolution={currentSettings.mediaResolution} setMediaResolution={(v) => updateSetting('mediaResolution', v)} - autoCanvasVisualization={currentSettings.autoCanvasVisualization ?? false} + transcriptionModelId={displaySettings.transcriptionModelId} setTranscriptionModelId={(v) => updateSetting('transcriptionModelId', v)} + generateQuadImages={displaySettings.generateQuadImages ?? false} setGenerateQuadImages={(v) => updateSetting('generateQuadImages', v)} + ttsVoice={displaySettings.ttsVoice} setTtsVoice={(v) => updateSetting('ttsVoice', v)} + systemInstruction={displaySettings.systemInstruction} setSystemInstruction={(v) => updateSetting('systemInstruction', v)} + temperature={displaySettings.temperature} setTemperature={(v) => updateSetting('temperature', v)} + topP={displaySettings.topP} setTopP={(v) => updateSetting('topP', v)} + showThoughts={displaySettings.showThoughts} setShowThoughts={(v) => updateSetting('showThoughts', v)} + thinkingBudget={displaySettings.thinkingBudget} setThinkingBudget={(v) => updateSetting('thinkingBudget', v)} + thinkingLevel={displaySettings.thinkingLevel} setThinkingLevel={(v) => updateSetting('thinkingLevel', v)} + safetySettings={displaySettings.safetySettings} setSafetySettings={(v) => updateSetting('safetySettings', v)} + mediaResolution={displaySettings.mediaResolution} setMediaResolution={(v) => updateSetting('mediaResolution', v)} + autoCanvasVisualization={displaySettings.autoCanvasVisualization ?? false} setAutoCanvasVisualization={(v) => updateSetting('autoCanvasVisualization', v)} - autoCanvasModelId={currentSettings.autoCanvasModelId || 'gemini-3-flash-preview'} + autoCanvasModelId={displaySettings.autoCanvasModelId || 'gemini-3-flash-preview'} setAutoCanvasModelId={(v) => updateSetting('autoCanvasModelId', v)} availableModels={availableModels} t={t as any} setAvailableModels={setAvailableModels} /> +
)} {activeTab === 'interface' && (
+
)} {activeTab === 'account' && (
updateSetting('useCustomApiConfig', val)} - apiKey={currentSettings.apiKey} + apiKey={displaySettings.apiKey} setApiKey={(val) => updateSetting('apiKey', val)} - apiProxyUrl={currentSettings.apiProxyUrl} + apiProxyUrl={displaySettings.apiProxyUrl} setApiProxyUrl={(val) => updateSetting('apiProxyUrl', val)} - useApiProxy={currentSettings.useApiProxy ?? false} + useApiProxy={displaySettings.useApiProxy ?? false} setUseApiProxy={(val) => updateSetting('useApiProxy', val)} availableModels={availableModels} t={t as any} /> +
)} {activeTab === 'data' && ( @@ -139,16 +182,16 @@ export const SettingsContent: React.FC = ({ />
)} - {activeTab === 'shortcuts' && ( + {activeTab === 'shortcuts' && (
- -
+
)} - {activeTab === 'about' && (
)} + {activeTab === 'about' && (
)} ); }; diff --git a/all-model-chat/apps/web/components/settings/SettingsModal.tsx b/all-model-chat/apps/web/components/settings/SettingsModal.tsx new file mode 100644 index 00000000..0002ed23 --- /dev/null +++ b/all-model-chat/apps/web/components/settings/SettingsModal.tsx @@ -0,0 +1,161 @@ + +import React from 'react'; +import { AppSettings, ModelOption } from '../../types'; +import { Theme } from '../../constants/themeConstants'; +import { translations } from '../../utils/appUtils'; +import { Modal } from '../shared/Modal'; +import { ConfirmationModal } from '../modals/ConfirmationModal'; +import { useSettingsLogic } from '../../hooks/features/useSettingsLogic'; +import { SettingsSidebar } from './SettingsSidebar'; +import { SettingsContent } from './SettingsContent'; + +interface SettingsModalProps { + isOpen: boolean; + onClose: () => void; + currentSettings: AppSettings; + availableModels: ModelOption[]; + availableThemes: Theme[]; + onSave: (newSettings: AppSettings) => void; + onClearAllHistory: () => void; + onClearCache: () => void; + onOpenLogViewer: () => void; + onInstallPwa: () => void; + isInstallable: boolean; + onImportSettings: (file: File) => void; + onExportSettings: () => void; + onImportHistory: (file: File) => void; + onExportHistory: () => void; + onImportScenarios: (file: File) => void; + onExportScenarios: () => void; + t: (key: keyof typeof translations) => string; + setAvailableModels: (models: ModelOption[]) => void; +} + +export const SettingsModal: React.FC = ({ + isOpen, onClose, currentSettings, availableModels, availableThemes, + onSave, onClearAllHistory, onClearCache, onOpenLogViewer, + onInstallPwa, isInstallable, t, + onImportSettings, onExportSettings, + onImportHistory, onExportHistory, + onImportScenarios, onExportScenarios, + setAvailableModels +}) => { + + const { + activeTab, + setActiveTab, + confirmConfig, + closeConfirm, + scrollContainerRef, + handleContentScroll, + handleResetToDefaults, + handleClearLogs, + handleRequestClearHistory, + handleRequestClearCache, + handleRequestImportHistory, + updateSetting, + handleModelChange, + tabs, + // New pending state exports + pendingSettings, + hasUnsavedChanges, + savePendingChanges, + discardPendingChanges, + handleClose, + requiresConfirmation + } = useSettingsLogic({ + isOpen, + onClose, + currentSettings, + onSave, + onClearAllHistory, + onClearCache, + onOpenLogViewer, + onImportHistory, + t + }); + + if (!isOpen) return null; + + return ( + <> + + + + {/* Content Area */} +
+ {/* Desktop Header */} +
+

+ {t(tabs.find(t => t.id === activeTab)?.labelKey as any)} +

+ {hasUnsavedChanges && requiresConfirmation && ( + + {t('settingsUnsavedChanges') || 'Unsaved'} + + )} +
+ + {/* Scrollable Content */} +
+ { onOpenLogViewer(); onClose(); }} + onClearLogs={handleClearLogs} + onReset={handleResetToDefaults} + onInstallPwa={onInstallPwa} + isInstallable={isInstallable} + onImportSettings={onImportSettings} + onExportSettings={onExportSettings} + onImportHistory={handleRequestImportHistory} + onExportHistory={onExportHistory} + onImportScenarios={onImportScenarios} + onExportScenarios={onExportScenarios} + hasUnsavedChanges={hasUnsavedChanges} + onSave={savePendingChanges} + onDiscard={discardPendingChanges} + requiresConfirmation={requiresConfirmation} + t={t} + /> +
+
+
+ + {confirmConfig.isOpen && ( + { confirmConfig.onConfirm(); closeConfirm(); }} + title={confirmConfig.title} + message={confirmConfig.message} + isDanger={confirmConfig.isDanger} + confirmLabel={confirmConfig.confirmLabel} + cancelLabel={t('settingsDiscardChanges') || t('cancel')} + /> + )} + + ); +}; diff --git a/all-model-chat/components/settings/SettingsSidebar.tsx b/all-model-chat/apps/web/components/settings/SettingsSidebar.tsx similarity index 100% rename from all-model-chat/components/settings/SettingsSidebar.tsx rename to all-model-chat/apps/web/components/settings/SettingsSidebar.tsx diff --git a/all-model-chat/components/settings/controls/ModelSelector.tsx b/all-model-chat/apps/web/components/settings/controls/ModelSelector.tsx similarity index 100% rename from all-model-chat/components/settings/controls/ModelSelector.tsx rename to all-model-chat/apps/web/components/settings/controls/ModelSelector.tsx diff --git a/all-model-chat/components/settings/controls/VoiceControl.tsx b/all-model-chat/apps/web/components/settings/controls/VoiceControl.tsx similarity index 100% rename from all-model-chat/components/settings/controls/VoiceControl.tsx rename to all-model-chat/apps/web/components/settings/controls/VoiceControl.tsx diff --git a/all-model-chat/components/settings/controls/model-selector/ModelListEditor.tsx b/all-model-chat/apps/web/components/settings/controls/model-selector/ModelListEditor.tsx similarity index 100% rename from all-model-chat/components/settings/controls/model-selector/ModelListEditor.tsx rename to all-model-chat/apps/web/components/settings/controls/model-selector/ModelListEditor.tsx diff --git a/all-model-chat/components/settings/controls/model-selector/ModelListEditorRow.tsx b/all-model-chat/apps/web/components/settings/controls/model-selector/ModelListEditorRow.tsx similarity index 100% rename from all-model-chat/components/settings/controls/model-selector/ModelListEditorRow.tsx rename to all-model-chat/apps/web/components/settings/controls/model-selector/ModelListEditorRow.tsx diff --git a/all-model-chat/components/settings/controls/model-selector/ModelListView.tsx b/all-model-chat/apps/web/components/settings/controls/model-selector/ModelListView.tsx similarity index 100% rename from all-model-chat/components/settings/controls/model-selector/ModelListView.tsx rename to all-model-chat/apps/web/components/settings/controls/model-selector/ModelListView.tsx diff --git a/all-model-chat/components/settings/controls/model-selector/ModelSelectorHeader.tsx b/all-model-chat/apps/web/components/settings/controls/model-selector/ModelSelectorHeader.tsx similarity index 100% rename from all-model-chat/components/settings/controls/model-selector/ModelSelectorHeader.tsx rename to all-model-chat/apps/web/components/settings/controls/model-selector/ModelSelectorHeader.tsx diff --git a/all-model-chat/components/settings/controls/thinking/LevelButton.tsx b/all-model-chat/apps/web/components/settings/controls/thinking/LevelButton.tsx similarity index 100% rename from all-model-chat/components/settings/controls/thinking/LevelButton.tsx rename to all-model-chat/apps/web/components/settings/controls/thinking/LevelButton.tsx diff --git a/all-model-chat/components/settings/controls/thinking/SparklesIcon.tsx b/all-model-chat/apps/web/components/settings/controls/thinking/SparklesIcon.tsx similarity index 100% rename from all-model-chat/components/settings/controls/thinking/SparklesIcon.tsx rename to all-model-chat/apps/web/components/settings/controls/thinking/SparklesIcon.tsx diff --git a/all-model-chat/components/settings/controls/thinking/ThinkingBudgetSlider.tsx b/all-model-chat/apps/web/components/settings/controls/thinking/ThinkingBudgetSlider.tsx similarity index 100% rename from all-model-chat/components/settings/controls/thinking/ThinkingBudgetSlider.tsx rename to all-model-chat/apps/web/components/settings/controls/thinking/ThinkingBudgetSlider.tsx diff --git a/all-model-chat/components/settings/controls/thinking/ThinkingControl.tsx b/all-model-chat/apps/web/components/settings/controls/thinking/ThinkingControl.tsx similarity index 100% rename from all-model-chat/components/settings/controls/thinking/ThinkingControl.tsx rename to all-model-chat/apps/web/components/settings/controls/thinking/ThinkingControl.tsx diff --git a/all-model-chat/components/settings/controls/thinking/ThinkingLevelSelector.tsx b/all-model-chat/apps/web/components/settings/controls/thinking/ThinkingLevelSelector.tsx similarity index 100% rename from all-model-chat/components/settings/controls/thinking/ThinkingLevelSelector.tsx rename to all-model-chat/apps/web/components/settings/controls/thinking/ThinkingLevelSelector.tsx diff --git a/all-model-chat/components/settings/controls/thinking/ThinkingModeSelector.tsx b/all-model-chat/apps/web/components/settings/controls/thinking/ThinkingModeSelector.tsx similarity index 100% rename from all-model-chat/components/settings/controls/thinking/ThinkingModeSelector.tsx rename to all-model-chat/apps/web/components/settings/controls/thinking/ThinkingModeSelector.tsx diff --git a/all-model-chat/components/settings/sections/AboutSection.tsx b/all-model-chat/apps/web/components/settings/sections/AboutSection.tsx similarity index 100% rename from all-model-chat/components/settings/sections/AboutSection.tsx rename to all-model-chat/apps/web/components/settings/sections/AboutSection.tsx diff --git a/all-model-chat/apps/web/components/settings/sections/ApiConfigSection.tsx b/all-model-chat/apps/web/components/settings/sections/ApiConfigSection.tsx new file mode 100644 index 00000000..b42f0aab --- /dev/null +++ b/all-model-chat/apps/web/components/settings/sections/ApiConfigSection.tsx @@ -0,0 +1,186 @@ + +import React, { useState, useEffect } from 'react'; +import { KeyRound } from 'lucide-react'; +import { useResponsiveValue } from '../../../hooks/useDevice'; +import { getClient } from '../../../services/api/baseApi'; +import { parseApiKeys } from '../../../utils/apiUtils'; +import { resolveBffEndpoint } from '../../../services/api/bffApi'; +import { ApiConfigToggle } from './api-config/ApiConfigToggle'; +import { ApiKeyInput } from './api-config/ApiKeyInput'; +import { ApiProxySettings } from './api-config/ApiProxySettings'; +import { ApiConnectionTester } from './api-config/ApiConnectionTester'; +import { ModelOption } from '../../../types'; + +interface ApiConfigSectionProps { + useCustomApiConfig: boolean; + setUseCustomApiConfig: (value: boolean) => void; + apiKey: string | null; + setApiKey: (value: string | null) => void; + apiProxyUrl: string | null; + setApiProxyUrl: (value: string | null) => void; + useApiProxy: boolean; + setUseApiProxy: (value: boolean) => void; + availableModels: ModelOption[]; + t: (key: string) => string; +} + +const CONNECTION_TEST_MODELS: ModelOption[] = [ + { id: 'gemini-3-flash-preview', name: 'Gemini 3 Flash Preview' }, + { id: 'gemini-3-pro-preview', name: 'Gemini 3 Pro Preview' }, + { id: 'gemini-2.5-flash-preview-09-2025', name: 'Gemini 2.5 Flash' }, + { id: 'gemini-2.5-flash-lite-preview-09-2025', name: 'Gemini 2.5 Flash Lite' }, + { id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro' }, + { id: 'gemma-3-27b-it', name: 'Gemma 3 27b IT' }, +]; + +export const ApiConfigSection: React.FC = ({ + useCustomApiConfig, + setUseCustomApiConfig, + apiKey, + setApiKey, + apiProxyUrl, + setApiProxyUrl, + useApiProxy, + setUseApiProxy, + availableModels, + t, +}) => { + // Test connection state + const [testStatus, setTestStatus] = useState<'idle' | 'testing' | 'success' | 'error'>('idle'); + const [testMessage, setTestMessage] = useState(null); + const [testModelId, setTestModelId] = useState('gemini-3-flash-preview'); + + // State to manage overflow visibility during transitions + const [allowOverflow, setAllowOverflow] = useState(useCustomApiConfig); + + const iconSize = useResponsiveValue(18, 20); + + useEffect(() => { + let timer: NodeJS.Timeout; + if (useCustomApiConfig) { + // Delay allowing overflow until transition matches duration (300ms) to prevent clipping artifacts during expansion + timer = setTimeout(() => setAllowOverflow(true), 300); + } else { + // Immediately hide overflow when collapsing to ensure clean animation + setAllowOverflow(false); + } + return () => clearTimeout(timer); + }, [useCustomApiConfig]); + + const handleTestConnection = async () => { + if (!useCustomApiConfig) { + setTestStatus('testing'); + setTestMessage(null); + try { + const healthResponse = await fetch(resolveBffEndpoint('/health'), { + method: 'GET', + }); + if (!healthResponse.ok) { + throw new Error(`BFF health check failed with status ${healthResponse.status}.`); + } + + const payload = (await healthResponse.json()) as { + provider?: { configuredKeyCount?: number }; + }; + const configuredKeyCount = Number(payload?.provider?.configuredKeyCount || 0); + if (configuredKeyCount <= 0) { + throw new Error('Backend API key is not configured.'); + } + + setTestStatus('success'); + } catch (error) { + setTestStatus('error'); + setTestMessage(error instanceof Error ? error.message : String(error)); + } + return; + } + + const keyToTest = apiKey; + if (!keyToTest) { + setTestStatus('error'); + setTestMessage("No API Key provided to test."); + return; + } + + // Handle multiple keys - pick first for test + const keys = parseApiKeys(keyToTest); + const firstKey = keys[0]; + + if (!firstKey) { + setTestStatus('error'); + setTestMessage("Invalid API Key format."); + return; + } + + const effectiveUrl = (useCustomApiConfig && useApiProxy && apiProxyUrl) ? apiProxyUrl : null; + + setTestStatus('testing'); + setTestMessage(null); + + try { + // Use the base API helper to get a client with sanitation logic + const ai = getClient(firstKey, effectiveUrl); + + const modelIdToUse = testModelId || 'gemini-3-flash-preview'; + + await ai.models.generateContent({ + model: modelIdToUse, + contents: 'Hello', + }); + + setTestStatus('success'); + } catch (error) { + setTestStatus('error'); + setTestMessage(error instanceof Error ? error.message : String(error)); + } + }; + + return ( +
+
+

+ + {t('settingsApiConfig')} +

+
+ +
+ + + {/* Content - collapsible area */} +
+
+ { setApiKey(val); setTestStatus('idle'); }} + t={t} + /> + + { setUseApiProxy(val); setTestStatus('idle'); }} + apiProxyUrl={apiProxyUrl} + setApiProxyUrl={(val) => { setApiProxyUrl(val); setTestStatus('idle'); }} + t={t} + /> + + +
+
+
+
+ ); +}; diff --git a/all-model-chat/components/settings/sections/AppearanceSection.tsx b/all-model-chat/apps/web/components/settings/sections/AppearanceSection.tsx similarity index 100% rename from all-model-chat/components/settings/sections/AppearanceSection.tsx rename to all-model-chat/apps/web/components/settings/sections/AppearanceSection.tsx diff --git a/all-model-chat/components/settings/sections/ChatBehaviorSection.tsx b/all-model-chat/apps/web/components/settings/sections/ChatBehaviorSection.tsx similarity index 100% rename from all-model-chat/components/settings/sections/ChatBehaviorSection.tsx rename to all-model-chat/apps/web/components/settings/sections/ChatBehaviorSection.tsx diff --git a/all-model-chat/components/settings/sections/DataManagementSection.tsx b/all-model-chat/apps/web/components/settings/sections/DataManagementSection.tsx similarity index 100% rename from all-model-chat/components/settings/sections/DataManagementSection.tsx rename to all-model-chat/apps/web/components/settings/sections/DataManagementSection.tsx diff --git a/all-model-chat/components/settings/sections/SafetySection.tsx b/all-model-chat/apps/web/components/settings/sections/SafetySection.tsx similarity index 100% rename from all-model-chat/components/settings/sections/SafetySection.tsx rename to all-model-chat/apps/web/components/settings/sections/SafetySection.tsx diff --git a/all-model-chat/components/settings/sections/ShortcutsSection.tsx b/all-model-chat/apps/web/components/settings/sections/ShortcutsSection.tsx similarity index 100% rename from all-model-chat/components/settings/sections/ShortcutsSection.tsx rename to all-model-chat/apps/web/components/settings/sections/ShortcutsSection.tsx diff --git a/all-model-chat/components/settings/sections/api-config/ApiConfigToggle.tsx b/all-model-chat/apps/web/components/settings/sections/api-config/ApiConfigToggle.tsx similarity index 68% rename from all-model-chat/components/settings/sections/api-config/ApiConfigToggle.tsx rename to all-model-chat/apps/web/components/settings/sections/api-config/ApiConfigToggle.tsx index 9ff6f492..7fe105a6 100644 --- a/all-model-chat/components/settings/sections/api-config/ApiConfigToggle.tsx +++ b/all-model-chat/apps/web/components/settings/sections/api-config/ApiConfigToggle.tsx @@ -1,19 +1,16 @@ import React from 'react'; -import { ShieldCheck } from 'lucide-react'; import { Toggle } from '../../../shared/Toggle'; interface ApiConfigToggleProps { useCustomApiConfig: boolean; setUseCustomApiConfig: (value: boolean) => void; - hasEnvKey: boolean; t: (key: string) => string; } export const ApiConfigToggle: React.FC = ({ useCustomApiConfig, setUseCustomApiConfig, - hasEnvKey, t }) => { const handleRowClick = () => { @@ -28,16 +25,11 @@ export const ApiConfigToggle: React.FC = ({
{t('settingsUseCustomApi')} - {hasEnvKey && !useCustomApiConfig && ( - - Env Key Active - - )} {useCustomApiConfig - ? (hasEnvKey ? 'Overriding environment API key' : 'Using your own API keys') - : (hasEnvKey ? t('apiConfig_default_info') : 'No API key found in environment. Enable custom key to proceed.') + ? 'Using your own API configuration from this panel.' + : 'Using backend API configuration (server-managed).' }
diff --git a/all-model-chat/components/settings/sections/api-config/ApiConnectionTester.tsx b/all-model-chat/apps/web/components/settings/sections/api-config/ApiConnectionTester.tsx similarity index 100% rename from all-model-chat/components/settings/sections/api-config/ApiConnectionTester.tsx rename to all-model-chat/apps/web/components/settings/sections/api-config/ApiConnectionTester.tsx diff --git a/all-model-chat/components/settings/sections/api-config/ApiKeyInput.tsx b/all-model-chat/apps/web/components/settings/sections/api-config/ApiKeyInput.tsx similarity index 100% rename from all-model-chat/components/settings/sections/api-config/ApiKeyInput.tsx rename to all-model-chat/apps/web/components/settings/sections/api-config/ApiKeyInput.tsx diff --git a/all-model-chat/components/settings/sections/api-config/ApiProxySettings.tsx b/all-model-chat/apps/web/components/settings/sections/api-config/ApiProxySettings.tsx similarity index 96% rename from all-model-chat/components/settings/sections/api-config/ApiProxySettings.tsx rename to all-model-chat/apps/web/components/settings/sections/api-config/ApiProxySettings.tsx index b085fce2..e760565a 100644 --- a/all-model-chat/components/settings/sections/api-config/ApiProxySettings.tsx +++ b/all-model-chat/apps/web/components/settings/sections/api-config/ApiProxySettings.tsx @@ -20,11 +20,11 @@ export const ApiProxySettings: React.FC = ({ t }) => { const inputBaseClasses = "w-full p-3 rounded-lg border transition-all duration-200 focus:ring-2 focus:ring-offset-0 text-sm custom-scrollbar font-mono"; - + const defaultBaseUrl = 'https://generativelanguage.googleapis.com/v1beta'; const defaultProxyUrl = 'https://api-proxy.de/gemini/v1beta'; const VERTEX_URL = "https://aiplatform.googleapis.com/v1"; - + const isVertexExpressActive = useApiProxy && apiProxyUrl === VERTEX_URL; const handleSetVertexExpress = () => { @@ -60,11 +60,10 @@ export const ApiProxySettings: React.FC = ({ + + {/* Render children if directory is expanded */} + {node.isDirectory && isExpanded && node.children && ( +
+ {node.children.map(child => ( + + ))} +
+ )} + + ); +}; + +export const FolderExplorer: React.FC = ({ + projectContext, + readState, + onClearProject, + onFileClick, + t, + themeId, +}) => { + const [expandedPaths, setExpandedPaths] = useState>(() => { + // Auto-expand first level + const initial = new Set(); + projectContext.treeNodes.forEach(node => { + if (node.isDirectory) initial.add(node.path); + }); + return initial; + }); + + const handleToggleExpand = useCallback((path: string) => { + setExpandedPaths(prev => { + const next = new Set(prev); + if (next.has(path)) { + next.delete(path); + } else { + next.add(path); + } + return next; + }); + }, []); + + const handleExpandAll = useCallback(() => { + const allDirs = new Set(); + const collectDirs = (nodes: FileTreeNode[]) => { + nodes.forEach(node => { + if (node.isDirectory) { + allDirs.add(node.path); + if (node.children) collectDirs(node.children); + } + }); + }; + collectDirs(projectContext.treeNodes); + setExpandedPaths(allDirs); + }, [projectContext.treeNodes]); + + const handleCollapseAll = useCallback(() => { + setExpandedPaths(new Set()); + }, []); + + return ( +
+ {/* Header */} +
+
+ + + {projectContext.rootName} + +
+
+ + + +
+
+ + {/* Stats bar */} +
+ {projectContext.totalFiles} files + {formatSize(projectContext.totalSize)} +
+ + {/* Tree view */} +
+ {projectContext.treeNodes.map(node => ( + + ))} +
+ + {/* Read files indicator */} + {readState && readState.readFiles.size > 0 && ( +
+
+ + AI read {readState.readFiles.size} file{readState.readFiles.size !== 1 ? 's' : ''} +
+
+ )} +
+ ); +}; diff --git a/all-model-chat/components/sidebar/GroupItem.tsx b/all-model-chat/apps/web/components/sidebar/GroupItem.tsx similarity index 100% rename from all-model-chat/components/sidebar/GroupItem.tsx rename to all-model-chat/apps/web/components/sidebar/GroupItem.tsx diff --git a/all-model-chat/components/sidebar/GroupItemMenu.tsx b/all-model-chat/apps/web/components/sidebar/GroupItemMenu.tsx similarity index 100% rename from all-model-chat/components/sidebar/GroupItemMenu.tsx rename to all-model-chat/apps/web/components/sidebar/GroupItemMenu.tsx diff --git a/all-model-chat/components/sidebar/HistorySidebar.tsx b/all-model-chat/apps/web/components/sidebar/HistorySidebar.tsx similarity index 100% rename from all-model-chat/components/sidebar/HistorySidebar.tsx rename to all-model-chat/apps/web/components/sidebar/HistorySidebar.tsx diff --git a/all-model-chat/components/sidebar/SessionItem.tsx b/all-model-chat/apps/web/components/sidebar/SessionItem.tsx similarity index 100% rename from all-model-chat/components/sidebar/SessionItem.tsx rename to all-model-chat/apps/web/components/sidebar/SessionItem.tsx diff --git a/all-model-chat/components/sidebar/SessionItemMenu.tsx b/all-model-chat/apps/web/components/sidebar/SessionItemMenu.tsx similarity index 100% rename from all-model-chat/components/sidebar/SessionItemMenu.tsx rename to all-model-chat/apps/web/components/sidebar/SessionItemMenu.tsx diff --git a/all-model-chat/components/sidebar/SidebarActions.tsx b/all-model-chat/apps/web/components/sidebar/SidebarActions.tsx similarity index 100% rename from all-model-chat/components/sidebar/SidebarActions.tsx rename to all-model-chat/apps/web/components/sidebar/SidebarActions.tsx diff --git a/all-model-chat/components/sidebar/SidebarHeader.tsx b/all-model-chat/apps/web/components/sidebar/SidebarHeader.tsx similarity index 100% rename from all-model-chat/components/sidebar/SidebarHeader.tsx rename to all-model-chat/apps/web/components/sidebar/SidebarHeader.tsx diff --git a/all-model-chat/constants/appConstants.ts b/all-model-chat/apps/web/constants/appConstants.ts similarity index 100% rename from all-model-chat/constants/appConstants.ts rename to all-model-chat/apps/web/constants/appConstants.ts diff --git a/all-model-chat/constants/defaultScenarios.ts b/all-model-chat/apps/web/constants/defaultScenarios.ts similarity index 100% rename from all-model-chat/constants/defaultScenarios.ts rename to all-model-chat/apps/web/constants/defaultScenarios.ts diff --git a/all-model-chat/constants/fileConstants.ts b/all-model-chat/apps/web/constants/fileConstants.ts similarity index 100% rename from all-model-chat/constants/fileConstants.ts rename to all-model-chat/apps/web/constants/fileConstants.ts diff --git a/all-model-chat/constants/modelConstants.ts b/all-model-chat/apps/web/constants/modelConstants.ts similarity index 100% rename from all-model-chat/constants/modelConstants.ts rename to all-model-chat/apps/web/constants/modelConstants.ts diff --git a/all-model-chat/constants/promptConstants.ts b/all-model-chat/apps/web/constants/promptConstants.ts similarity index 100% rename from all-model-chat/constants/promptConstants.ts rename to all-model-chat/apps/web/constants/promptConstants.ts diff --git a/all-model-chat/constants/prompts/canvas.ts b/all-model-chat/apps/web/constants/prompts/canvas.ts similarity index 100% rename from all-model-chat/constants/prompts/canvas.ts rename to all-model-chat/apps/web/constants/prompts/canvas.ts diff --git a/all-model-chat/constants/prompts/deepSearch.ts b/all-model-chat/apps/web/constants/prompts/deepSearch.ts similarity index 100% rename from all-model-chat/constants/prompts/deepSearch.ts rename to all-model-chat/apps/web/constants/prompts/deepSearch.ts diff --git a/all-model-chat/constants/scenarios/adventure.ts b/all-model-chat/apps/web/constants/scenarios/adventure.ts similarity index 100% rename from all-model-chat/constants/scenarios/adventure.ts rename to all-model-chat/apps/web/constants/scenarios/adventure.ts diff --git a/all-model-chat/constants/scenarios/demo.ts b/all-model-chat/apps/web/constants/scenarios/demo.ts similarity index 100% rename from all-model-chat/constants/scenarios/demo.ts rename to all-model-chat/apps/web/constants/scenarios/demo.ts diff --git a/all-model-chat/constants/scenarios/jailbreak.ts b/all-model-chat/apps/web/constants/scenarios/jailbreak.ts similarity index 100% rename from all-model-chat/constants/scenarios/jailbreak.ts rename to all-model-chat/apps/web/constants/scenarios/jailbreak.ts diff --git a/all-model-chat/constants/scenarios/utility.ts b/all-model-chat/apps/web/constants/scenarios/utility.ts similarity index 100% rename from all-model-chat/constants/scenarios/utility.ts rename to all-model-chat/apps/web/constants/scenarios/utility.ts diff --git a/all-model-chat/constants/shortcuts.ts b/all-model-chat/apps/web/constants/shortcuts.ts similarity index 100% rename from all-model-chat/constants/shortcuts.ts rename to all-model-chat/apps/web/constants/shortcuts.ts diff --git a/all-model-chat/constants/specialPrompts.ts b/all-model-chat/apps/web/constants/specialPrompts.ts similarity index 100% rename from all-model-chat/constants/specialPrompts.ts rename to all-model-chat/apps/web/constants/specialPrompts.ts diff --git a/all-model-chat/constants/themeConstants.ts b/all-model-chat/apps/web/constants/themeConstants.ts similarity index 100% rename from all-model-chat/constants/themeConstants.ts rename to all-model-chat/apps/web/constants/themeConstants.ts diff --git a/all-model-chat/contexts/WindowContext.tsx b/all-model-chat/apps/web/contexts/WindowContext.tsx similarity index 100% rename from all-model-chat/contexts/WindowContext.tsx rename to all-model-chat/apps/web/contexts/WindowContext.tsx diff --git a/all-model-chat/hooks/app/logic/useAppHandlers.ts b/all-model-chat/apps/web/hooks/app/logic/useAppHandlers.ts similarity index 100% rename from all-model-chat/hooks/app/logic/useAppHandlers.ts rename to all-model-chat/apps/web/hooks/app/logic/useAppHandlers.ts diff --git a/all-model-chat/hooks/app/logic/useAppInitialization.ts b/all-model-chat/apps/web/hooks/app/logic/useAppInitialization.ts similarity index 100% rename from all-model-chat/hooks/app/logic/useAppInitialization.ts rename to all-model-chat/apps/web/hooks/app/logic/useAppInitialization.ts diff --git a/all-model-chat/hooks/app/logic/useAppSidePanel.ts b/all-model-chat/apps/web/hooks/app/logic/useAppSidePanel.ts similarity index 100% rename from all-model-chat/hooks/app/logic/useAppSidePanel.ts rename to all-model-chat/apps/web/hooks/app/logic/useAppSidePanel.ts diff --git a/all-model-chat/hooks/app/logic/useAppTitle.ts b/all-model-chat/apps/web/hooks/app/logic/useAppTitle.ts similarity index 100% rename from all-model-chat/hooks/app/logic/useAppTitle.ts rename to all-model-chat/apps/web/hooks/app/logic/useAppTitle.ts diff --git a/all-model-chat/hooks/app/props/useAppModalsProps.ts b/all-model-chat/apps/web/hooks/app/props/useAppModalsProps.ts similarity index 100% rename from all-model-chat/hooks/app/props/useAppModalsProps.ts rename to all-model-chat/apps/web/hooks/app/props/useAppModalsProps.ts diff --git a/all-model-chat/hooks/app/props/useChatAreaProps.ts b/all-model-chat/apps/web/hooks/app/props/useChatAreaProps.ts similarity index 96% rename from all-model-chat/hooks/app/props/useChatAreaProps.ts rename to all-model-chat/apps/web/hooks/app/props/useChatAreaProps.ts index 7f03126b..61d6b6c7 100644 --- a/all-model-chat/hooks/app/props/useChatAreaProps.ts +++ b/all-model-chat/apps/web/hooks/app/props/useChatAreaProps.ts @@ -135,9 +135,15 @@ export const useChatAreaProps = (logic: ReturnType) => { onAddUserMessage: chatState.handleAddUserMessage, onLiveTranscript: chatState.handleLiveTranscript, t, + + // Agentic folder access + projectContext: chatState.projectContext, + projectContextReadState: chatState.projectContextReadState, + onProjectContextCreated: chatState.handleProjectContextCreated, + onClearProjectContext: chatState.handleClearProjectContext, }), [ - chatState, uiState, appSettings, currentTheme, language, t, sessionTitle, - pipState, handleLoadCanvasPromptAndSave, handleToggleBBoxMode, handleToggleGuideMode, handleSuggestionClick, handleSetThinkingLevel, + chatState, uiState, appSettings, currentTheme, language, t, sessionTitle, + pipState, handleLoadCanvasPromptAndSave, handleToggleBBoxMode, handleToggleGuideMode, handleSuggestionClick, handleSetThinkingLevel, handleOpenSidePanel, getCurrentModelDisplayName, logic.setAppSettings ]); }; \ No newline at end of file diff --git a/all-model-chat/hooks/app/props/useSidebarProps.ts b/all-model-chat/apps/web/hooks/app/props/useSidebarProps.ts similarity index 100% rename from all-model-chat/hooks/app/props/useSidebarProps.ts rename to all-model-chat/apps/web/hooks/app/props/useSidebarProps.ts diff --git a/all-model-chat/hooks/app/useAppLogic.ts b/all-model-chat/apps/web/hooks/app/useAppLogic.ts similarity index 100% rename from all-model-chat/hooks/app/useAppLogic.ts rename to all-model-chat/apps/web/hooks/app/useAppLogic.ts diff --git a/all-model-chat/hooks/app/useAppProps.ts b/all-model-chat/apps/web/hooks/app/useAppProps.ts similarity index 100% rename from all-model-chat/hooks/app/useAppProps.ts rename to all-model-chat/apps/web/hooks/app/useAppProps.ts diff --git a/all-model-chat/hooks/chat-input/handlers/useFileManagementHandlers.ts b/all-model-chat/apps/web/hooks/chat-input/handlers/useFileManagementHandlers.ts similarity index 100% rename from all-model-chat/hooks/chat-input/handlers/useFileManagementHandlers.ts rename to all-model-chat/apps/web/hooks/chat-input/handlers/useFileManagementHandlers.ts diff --git a/all-model-chat/apps/web/hooks/chat-input/handlers/useFileSelectionHandlers.ts b/all-model-chat/apps/web/hooks/chat-input/handlers/useFileSelectionHandlers.ts new file mode 100644 index 00000000..51d14722 --- /dev/null +++ b/all-model-chat/apps/web/hooks/chat-input/handlers/useFileSelectionHandlers.ts @@ -0,0 +1,115 @@ + +import { useCallback, Dispatch, SetStateAction } from 'react'; +import { UploadedFile, ProjectContext } from '../../../types'; +import { generateUniqueId } from '../../../utils/appUtils'; +import { generateFolderContext, buildProjectContext, buildProjectContextFromZip } from '../../../utils/folderImportUtils'; + +interface UseFileSelectionHandlersProps { + onProcessFiles: (files: FileList | File[]) => Promise; + onProjectContextCreated?: (context: ProjectContext) => void; + setSelectedFiles: Dispatch>; + setAppFileError: (error: string | null) => void; + setIsConverting: Dispatch>; + justInitiatedFileOpRef: React.MutableRefObject; + fileInputRef: React.RefObject; + imageInputRef: React.RefObject; + folderInputRef: React.RefObject; + zipInputRef: React.RefObject; +} + +export const useFileSelectionHandlers = ({ + onProcessFiles, + onProjectContextCreated, + setSelectedFiles, + setAppFileError, + setIsConverting, + justInitiatedFileOpRef, + fileInputRef, + imageInputRef, + folderInputRef, + zipInputRef, +}: UseFileSelectionHandlersProps) => { + + const handleFolderZipFiles = useCallback(async (files: FileList | File[]) => { + const filesArray = Array.isArray(files) ? files : Array.from(files); + if (filesArray.length === 0) return; + + const isFolderImport = filesArray.some(file => !!(file as any).webkitRelativePath); + const isZipImport = !isFolderImport && filesArray.every(file => file.name.toLowerCase().endsWith('.zip')); + + const tempId = generateUniqueId(); + setIsConverting(true); + setSelectedFiles(prev => [...prev, { + id: tempId, + name: 'Processing import...', + type: isZipImport ? 'application/zip' : 'application/x-directory', + size: 0, + isProcessing: true, + uploadState: 'pending' + }]); + + try { + justInitiatedFileOpRef.current = true; + + if (onProjectContextCreated) { + if (isFolderImport) { + const fileEntries = filesArray.map(file => ({ + file, + path: (file as any).webkitRelativePath || file.name + })); + const projectContext = buildProjectContext(fileEntries); + onProjectContextCreated(projectContext); + } else if (isZipImport) { + for (const zipFile of filesArray) { + const projectContext = await buildProjectContextFromZip(zipFile); + onProjectContextCreated(projectContext); + } + } else { + await onProcessFiles(filesArray); + } + } else { + if (isFolderImport) { + const contextFile = await generateFolderContext(filesArray); + await onProcessFiles([contextFile]); + } else { + await onProcessFiles(filesArray); + } + } + } catch (e) { + console.error(e); + setAppFileError(isZipImport ? "Failed to process zip file." : "Failed to process folder structure."); + } finally { + setSelectedFiles(prev => prev.filter(f => f.id !== tempId)); + setIsConverting(false); + } + }, [buildProjectContextFromZip, generateFolderContext, onProcessFiles, onProjectContextCreated, setAppFileError, setIsConverting, setSelectedFiles, justInitiatedFileOpRef]); + + const handleFileChange = useCallback(async (event: React.ChangeEvent) => { + if (event.target.files?.length) { + justInitiatedFileOpRef.current = true; + await onProcessFiles(event.target.files); + } + if (fileInputRef.current) fileInputRef.current.value = ""; + if (imageInputRef.current) imageInputRef.current.value = ""; + }, [onProcessFiles, justInitiatedFileOpRef, fileInputRef, imageInputRef]); + + const handleFolderChange = useCallback(async (event: React.ChangeEvent) => { + if (event.target.files?.length) { + await handleFolderZipFiles(event.target.files); + } + if (folderInputRef.current) folderInputRef.current.value = ""; + }, [handleFolderZipFiles, folderInputRef]); + + const handleZipChange = useCallback(async (event: React.ChangeEvent) => { + if (event.target.files?.length) { + await handleFolderZipFiles(event.target.files); + } + if (zipInputRef.current) zipInputRef.current.value = ""; + }, [handleFolderZipFiles, zipInputRef]); + + return { + handleFileChange, + handleFolderChange, + handleZipChange, + }; +}; diff --git a/all-model-chat/hooks/chat-input/handlers/useInputAndPasteHandlers.ts b/all-model-chat/apps/web/hooks/chat-input/handlers/useInputAndPasteHandlers.ts similarity index 100% rename from all-model-chat/hooks/chat-input/handlers/useInputAndPasteHandlers.ts rename to all-model-chat/apps/web/hooks/chat-input/handlers/useInputAndPasteHandlers.ts diff --git a/all-model-chat/hooks/chat-input/handlers/useKeyboardHandlers.ts b/all-model-chat/apps/web/hooks/chat-input/handlers/useKeyboardHandlers.ts similarity index 100% rename from all-model-chat/hooks/chat-input/handlers/useKeyboardHandlers.ts rename to all-model-chat/apps/web/hooks/chat-input/handlers/useKeyboardHandlers.ts diff --git a/all-model-chat/hooks/chat-input/handlers/useSubmissionHandlers.ts b/all-model-chat/apps/web/hooks/chat-input/handlers/useSubmissionHandlers.ts similarity index 100% rename from all-model-chat/hooks/chat-input/handlers/useSubmissionHandlers.ts rename to all-model-chat/apps/web/hooks/chat-input/handlers/useSubmissionHandlers.ts diff --git a/all-model-chat/hooks/chat-input/useChatInputController.ts b/all-model-chat/apps/web/hooks/chat-input/useChatInputController.ts similarity index 100% rename from all-model-chat/hooks/chat-input/useChatInputController.ts rename to all-model-chat/apps/web/hooks/chat-input/useChatInputController.ts diff --git a/all-model-chat/hooks/chat-input/useChatInputEffects.ts b/all-model-chat/apps/web/hooks/chat-input/useChatInputEffects.ts similarity index 100% rename from all-model-chat/hooks/chat-input/useChatInputEffects.ts rename to all-model-chat/apps/web/hooks/chat-input/useChatInputEffects.ts diff --git a/all-model-chat/apps/web/hooks/chat-input/useChatInputHandlers.ts b/all-model-chat/apps/web/hooks/chat-input/useChatInputHandlers.ts new file mode 100644 index 00000000..4cdcdbd4 --- /dev/null +++ b/all-model-chat/apps/web/hooks/chat-input/useChatInputHandlers.ts @@ -0,0 +1,232 @@ + +import React, { useMemo } from 'react'; +import { UploadedFile, AppSettings, ChatSettings as IndividualChatSettings, ProjectContext } from '../../types'; +import { Command } from '../../components/chat/input/SlashCommandMenu'; +import { useFileSelectionHandlers } from './handlers/useFileSelectionHandlers'; +import { useInputAndPasteHandlers } from './handlers/useInputAndPasteHandlers'; +import { useSubmissionHandlers } from './handlers/useSubmissionHandlers'; +import { useKeyboardHandlers } from './handlers/useKeyboardHandlers'; +import { useFileManagementHandlers } from './handlers/useFileManagementHandlers'; + +interface UseChatInputHandlersProps { + // State & Setters + inputText: string; + setInputText: React.Dispatch>; + quotes: string[]; + setQuotes: React.Dispatch>; + fileIdInput: string; + setFileIdInput: React.Dispatch>; + urlInput: string; + setUrlInput: React.Dispatch>; + selectedFiles: UploadedFile[]; + setSelectedFiles: (files: UploadedFile[] | ((prevFiles: UploadedFile[]) => UploadedFile[])) => void; + previewFile: UploadedFile | null; + setPreviewFile: React.Dispatch>; + + // UI State + isAddingById: boolean; + setIsAddingById: React.Dispatch>; + isAddingByUrl: boolean; + setIsAddingByUrl: React.Dispatch>; + isTranslating: boolean; + setIsTranslating: React.Dispatch>; + isConverting: boolean; + setIsConverting: React.Dispatch>; + isLoading: boolean; + isFullscreen: boolean; + setIsFullscreen: React.Dispatch>; + setIsAnimatingSend: React.Dispatch>; + setIsWaitingForUpload: React.Dispatch>; + isEditing: boolean; + editMode: 'update' | 'resend'; + editingMessageId: string | null; + setEditingMessageId: (id: string | null) => void; + + // Modals + showCreateTextFileEditor: boolean; + showCamera: boolean; + showRecorder: boolean; + setShowAddByUrlInput: React.Dispatch>; + setShowAddByIdInput: React.Dispatch>; + + // Refs + textareaRef: React.RefObject; + fileInputRef: React.RefObject; + imageInputRef: React.RefObject; + folderInputRef: React.RefObject; + zipInputRef: React.RefObject; + justInitiatedFileOpRef: React.MutableRefObject; + isComposingRef: React.MutableRefObject; + + // Settings & Config + appSettings: AppSettings; + currentChatSettings: IndividualChatSettings; + setCurrentChatSettings: (updater: (prevSettings: IndividualChatSettings) => IndividualChatSettings) => void; + setAppFileError: (error: string | null) => void; + + // Slash Commands + slashCommandState: { isOpen: boolean; filteredCommands: Command[]; selectedIndex: number; }; + setSlashCommandState: React.Dispatch>; + handleCommandSelect: (command: Command) => void; + handleSlashCommandExecution: (text: string) => void; + handleSlashInputChange: (value: string) => void; + + // Core Actions + onProcessFiles: (files: FileList | File[]) => Promise; + onProjectContextCreated?: (context: ProjectContext) => void; + onAddFileById: (fileId: string) => Promise; + onSendMessage: (text: string, options?: { isFastMode?: boolean }) => void; + onMessageSent: () => void; + onUpdateMessageContent: (messageId: string, content: string) => void; + clearCurrentDraft: () => void; + handleToggleFullscreen: () => void; + onStopGenerating: () => void; + onCancelEdit: () => void; + onEditLastUserMessage: () => void; + + // Environment + isMobile: boolean; + isDesktop: boolean; + canSend: boolean; +} + +export const useChatInputHandlers = (props: UseChatInputHandlersProps) => { + // 1. File Selection Logic + const { handleFileChange, handleFolderChange, handleZipChange } = useFileSelectionHandlers({ + onProcessFiles: props.onProcessFiles, + onProjectContextCreated: props.onProjectContextCreated, + setSelectedFiles: props.setSelectedFiles, + setAppFileError: props.setAppFileError, + setIsConverting: props.setIsConverting, + justInitiatedFileOpRef: props.justInitiatedFileOpRef, + fileInputRef: props.fileInputRef, + imageInputRef: props.imageInputRef, + folderInputRef: props.folderInputRef, + zipInputRef: props.zipInputRef, + }); + + // 2. Input, Paste, URL logic + const { handleAddUrl, handlePaste, handlePasteAction, handleInputChange } = useInputAndPasteHandlers({ + setInputText: props.setInputText, + setUrlInput: props.setUrlInput, + setShowAddByUrlInput: props.setShowAddByUrlInput, + setAppFileError: props.setAppFileError, + setSelectedFiles: props.setSelectedFiles, + textareaRef: props.textareaRef, + justInitiatedFileOpRef: props.justInitiatedFileOpRef, + onProcessFiles: props.onProcessFiles, + isAddingById: props.isAddingById, + showCreateTextFileEditor: props.showCreateTextFileEditor, + showCamera: props.showCamera, + showRecorder: props.showRecorder, + handleSlashInputChange: props.handleSlashInputChange, + isPasteRichTextAsMarkdownEnabled: props.appSettings.isPasteRichTextAsMarkdownEnabled ?? true, + isPasteAsTextFileEnabled: props.appSettings.isPasteAsTextFileEnabled ?? true, + }); + + // 3. Submission & Translation + const { handleSubmit, handleTranslate, handleFastSubmit } = useSubmissionHandlers({ + canSend: props.canSend, + selectedFiles: props.selectedFiles, + setIsWaitingForUpload: props.setIsWaitingForUpload, + isEditing: props.isEditing, + editMode: props.editMode, + editingMessageId: props.editingMessageId, + inputText: props.inputText, + quotes: props.quotes, + setInputText: props.setInputText, + setQuotes: props.setQuotes, + onUpdateMessageContent: props.onUpdateMessageContent, + setEditingMessageId: props.setEditingMessageId, + onMessageSent: props.onMessageSent, + clearCurrentDraft: props.clearCurrentDraft, + onSendMessage: props.onSendMessage, + setIsAnimatingSend: props.setIsAnimatingSend, + isFullscreen: props.isFullscreen, + setIsFullscreen: props.setIsFullscreen, + isTranslating: props.isTranslating, + setIsTranslating: props.setIsTranslating, + setAppFileError: props.setAppFileError, + appSettings: props.appSettings, + currentChatSettings: props.currentChatSettings, + }); + + // 4. Keyboard Handling + const { handleKeyDown } = useKeyboardHandlers({ + appSettings: props.appSettings, + isComposingRef: props.isComposingRef, + slashCommandState: props.slashCommandState, + setSlashCommandState: props.setSlashCommandState, + handleCommandSelect: props.handleCommandSelect, + inputText: props.inputText, + setInputText: props.setInputText, + isMobile: props.isMobile, + isDesktop: props.isDesktop, + handleSlashCommandExecution: props.handleSlashCommandExecution, + canSend: props.canSend, + handleSubmit, + isFullscreen: props.isFullscreen, + handleToggleFullscreen: props.handleToggleFullscreen, + isLoading: props.isLoading, + onStopGenerating: props.onStopGenerating, + isEditing: props.isEditing, + onCancelEdit: props.onCancelEdit, + onEditLastUserMessage: props.onEditLastUserMessage, + }); + + // 5. File Management (Remove, Add ID, Config, Preview Nav) + const { + removeSelectedFile, + handleAddFileByIdSubmit, + handleToggleToolAndFocus, + handleSaveFileConfig, + handlePrevImage, + handleNextImage, + inputImages, + currentImageIndex + } = useFileManagementHandlers({ + selectedFiles: props.selectedFiles, + setSelectedFiles: props.setSelectedFiles, + fileIdInput: props.fileIdInput, + isAddingById: props.isAddingById, + isLoading: props.isLoading, + setIsAddingById: props.setIsAddingById, + justInitiatedFileOpRef: props.justInitiatedFileOpRef, + onAddFileById: props.onAddFileById, + setFileIdInput: props.setFileIdInput, + textareaRef: props.textareaRef, + previewFile: props.previewFile, + setPreviewFile: props.setPreviewFile, + }); + + // Memoize the return object to stabilize prop references downstream + return useMemo(() => ({ + handleFileChange, + handleFolderChange, + handleZipChange, + handleAddUrl, + handlePaste, + handlePasteAction, + handleInputChange, + handleSubmit, + handleFastSubmit, + handleTranslate, + handleKeyDown, + removeSelectedFile, + handleAddFileByIdSubmit, + handleToggleToolAndFocus, + handleSaveFileConfig, + handlePrevImage, + handleNextImage, + inputImages, + currentImageIndex + }), [ + handleFileChange, handleFolderChange, handleZipChange, + handleAddUrl, handlePaste, handlePasteAction, handleInputChange, + handleSubmit, handleFastSubmit, handleTranslate, handleKeyDown, + removeSelectedFile, handleAddFileByIdSubmit, + handleToggleToolAndFocus, handleSaveFileConfig, + handlePrevImage, handleNextImage, + inputImages, currentImageIndex + ]); +}; diff --git a/all-model-chat/hooks/chat-input/useChatInputHeight.ts b/all-model-chat/apps/web/hooks/chat-input/useChatInputHeight.ts similarity index 100% rename from all-model-chat/hooks/chat-input/useChatInputHeight.ts rename to all-model-chat/apps/web/hooks/chat-input/useChatInputHeight.ts diff --git a/all-model-chat/hooks/chat-input/useChatInputLocalState.ts b/all-model-chat/apps/web/hooks/chat-input/useChatInputLocalState.ts similarity index 100% rename from all-model-chat/hooks/chat-input/useChatInputLocalState.ts rename to all-model-chat/apps/web/hooks/chat-input/useChatInputLocalState.ts diff --git a/all-model-chat/hooks/chat-input/useChatInputLogic.ts b/all-model-chat/apps/web/hooks/chat-input/useChatInputLogic.ts similarity index 92% rename from all-model-chat/hooks/chat-input/useChatInputLogic.ts rename to all-model-chat/apps/web/hooks/chat-input/useChatInputLogic.ts index 73b42a60..48f910e8 100644 --- a/all-model-chat/hooks/chat-input/useChatInputLogic.ts +++ b/all-model-chat/apps/web/hooks/chat-input/useChatInputLogic.ts @@ -1,18 +1,15 @@ - -import React, { useCallback, useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { ChatInputProps } from '../../types'; -import { useChatInputState } from './useChatInputState'; -import { useIsDesktop } from '../useDevice'; import { useWindowContext } from '../../contexts/WindowContext'; +import { useIsDesktop } from '../useDevice'; import { useModelCapabilities } from '../useModelCapabilities'; -import { useChatInputModals } from './useChatInputModals'; +import { useLiveAPI } from '../useLiveAPI'; import { useVoiceInput } from '../useVoiceInput'; import { useSlashCommands } from '../useSlashCommands'; -import { useChatInputEffects } from './useChatInputEffects'; +import { useChatInputState } from './useChatInputState'; +import { useChatInputModals } from './useChatInputModals'; import { useChatInputLocalState } from './useChatInputLocalState'; -import { useLiveAPI } from '../useLiveAPI'; - -// Direct imports of sub-handlers (Flattening the architecture) +import { useChatInputEffects } from './useChatInputEffects'; import { useFileSelectionHandlers } from './handlers/useFileSelectionHandlers'; import { useInputAndPasteHandlers } from './handlers/useInputAndPasteHandlers'; import { useSubmissionHandlers } from './handlers/useSubmissionHandlers'; @@ -21,21 +18,46 @@ import { useFileManagementHandlers } from './handlers/useFileManagementHandlers' export const useChatInputLogic = (props: ChatInputProps) => { const { - appSettings, currentChatSettings, activeSessionId, isEditing, - onProcessFiles, t, commandedInput, onSendMessage, onMessageSent, - setEditingMessageId, onTranscribeAudio, onUpdateMessageContent, - editingMessageId, editMode, onCancelEdit, onStopGenerating, - onToggleGoogleSearch, onToggleDeepSearch, onToggleCodeExecution, - onToggleUrlContext, onClearChat, onNewChat, onOpenSettings, - onToggleCanvasPrompt, onTogglePinCurrentSession, onRetryLastTurn, - onSelectModel, availableModels, onEditLastUserMessage, onTogglePip, - setCurrentChatSettings, onAddUserMessage, onLiveTranscript, + appSettings, + currentChatSettings, + activeSessionId, + commandedInput, + onSendMessage, + onMessageSent, + setEditingMessageId, + onTranscribeAudio, + onUpdateMessageContent, + editingMessageId, + editMode, + onCancelEdit, + onStopGenerating, + onProcessFiles, + onProjectContextCreated, + onToggleGoogleSearch, + onToggleDeepSearch, + onToggleCodeExecution, + onToggleUrlContext, + onClearChat, + onNewChat, + onOpenSettings, + onToggleCanvasPrompt, + onTogglePinCurrentSession, + onRetryLastTurn, + onSelectModel, + availableModels, + onEditLastUserMessage, + onTogglePip, + setCurrentChatSettings, + onAddUserMessage, + onLiveTranscript, + isEditing, + t, } = props; + const isDesktop = useIsDesktop(); + // 1. State Management const inputState = useChatInputState(activeSessionId, isEditing); - - const isDesktop = useIsDesktop(); const { document: targetDocument } = useWindowContext(); // 2. Capabilities @@ -130,6 +152,7 @@ export const useChatInputLogic = (props: ChatInputProps) => { const fileSelectionHandlers = useFileSelectionHandlers({ onProcessFiles: props.onProcessFiles, + onProjectContextCreated, setSelectedFiles: props.setSelectedFiles, setAppFileError: props.setAppFileError, setIsConverting: localFileState.setIsConverting, diff --git a/all-model-chat/hooks/chat-input/useChatInputModals.ts b/all-model-chat/apps/web/hooks/chat-input/useChatInputModals.ts similarity index 100% rename from all-model-chat/hooks/chat-input/useChatInputModals.ts rename to all-model-chat/apps/web/hooks/chat-input/useChatInputModals.ts diff --git a/all-model-chat/hooks/chat-input/useChatInputPropsBuilder.ts b/all-model-chat/apps/web/hooks/chat-input/useChatInputPropsBuilder.ts similarity index 100% rename from all-model-chat/hooks/chat-input/useChatInputPropsBuilder.ts rename to all-model-chat/apps/web/hooks/chat-input/useChatInputPropsBuilder.ts diff --git a/all-model-chat/hooks/chat-input/useChatInputState.ts b/all-model-chat/apps/web/hooks/chat-input/useChatInputState.ts similarity index 100% rename from all-model-chat/hooks/chat-input/useChatInputState.ts rename to all-model-chat/apps/web/hooks/chat-input/useChatInputState.ts diff --git a/all-model-chat/hooks/chat-stream/processors.ts b/all-model-chat/apps/web/hooks/chat-stream/processors.ts similarity index 100% rename from all-model-chat/hooks/chat-stream/processors.ts rename to all-model-chat/apps/web/hooks/chat-stream/processors.ts diff --git a/all-model-chat/hooks/chat-stream/utils.ts b/all-model-chat/apps/web/hooks/chat-stream/utils.ts similarity index 100% rename from all-model-chat/hooks/chat-stream/utils.ts rename to all-model-chat/apps/web/hooks/chat-stream/utils.ts diff --git a/all-model-chat/hooks/chat/actions/useAudioActions.ts b/all-model-chat/apps/web/hooks/chat/actions/useAudioActions.ts similarity index 100% rename from all-model-chat/hooks/chat/actions/useAudioActions.ts rename to all-model-chat/apps/web/hooks/chat/actions/useAudioActions.ts diff --git a/all-model-chat/hooks/chat/actions/useChatSessionActions.ts b/all-model-chat/apps/web/hooks/chat/actions/useChatSessionActions.ts similarity index 100% rename from all-model-chat/hooks/chat/actions/useChatSessionActions.ts rename to all-model-chat/apps/web/hooks/chat/actions/useChatSessionActions.ts diff --git a/all-model-chat/hooks/chat/actions/useMessageUpdates.ts b/all-model-chat/apps/web/hooks/chat/actions/useMessageUpdates.ts similarity index 100% rename from all-model-chat/hooks/chat/actions/useMessageUpdates.ts rename to all-model-chat/apps/web/hooks/chat/actions/useMessageUpdates.ts diff --git a/all-model-chat/hooks/chat/actions/useModelSelection.ts b/all-model-chat/apps/web/hooks/chat/actions/useModelSelection.ts similarity index 100% rename from all-model-chat/hooks/chat/actions/useModelSelection.ts rename to all-model-chat/apps/web/hooks/chat/actions/useModelSelection.ts diff --git a/all-model-chat/hooks/chat/history/useGroupActions.ts b/all-model-chat/apps/web/hooks/chat/history/useGroupActions.ts similarity index 100% rename from all-model-chat/hooks/chat/history/useGroupActions.ts rename to all-model-chat/apps/web/hooks/chat/history/useGroupActions.ts diff --git a/all-model-chat/hooks/chat/history/useHistoryClearer.ts b/all-model-chat/apps/web/hooks/chat/history/useHistoryClearer.ts similarity index 100% rename from all-model-chat/hooks/chat/history/useHistoryClearer.ts rename to all-model-chat/apps/web/hooks/chat/history/useHistoryClearer.ts diff --git a/all-model-chat/hooks/chat/history/useSessionActions.ts b/all-model-chat/apps/web/hooks/chat/history/useSessionActions.ts similarity index 100% rename from all-model-chat/hooks/chat/history/useSessionActions.ts rename to all-model-chat/apps/web/hooks/chat/history/useSessionActions.ts diff --git a/all-model-chat/hooks/chat/history/useSessionLoader.ts b/all-model-chat/apps/web/hooks/chat/history/useSessionLoader.ts similarity index 87% rename from all-model-chat/hooks/chat/history/useSessionLoader.ts rename to all-model-chat/apps/web/hooks/chat/history/useSessionLoader.ts index 1397111e..01bd2ca3 100644 --- a/all-model-chat/hooks/chat/history/useSessionLoader.ts +++ b/all-model-chat/apps/web/hooks/chat/history/useSessionLoader.ts @@ -166,6 +166,11 @@ export const useSessionLoader = ({ const storedActiveId = sessionStorage.getItem(ACTIVE_CHAT_SESSION_ID_KEY); if (storedActiveId && metadataList.some(s => s.id === storedActiveId)) { initialActiveId = storedActiveId; + } else { + const dbActiveId = await dbService.getActiveSessionId(); + if (dbActiveId && metadataList.some(s => s.id === dbActiveId)) { + initialActiveId = dbActiveId; + } } } @@ -198,9 +203,26 @@ export const useSessionLoader = ({ setSavedGroups(groups.map(g => ({...g, isExpanded: g.isExpanded ?? true}))); if (!initialActiveId) { - // Fallback: New Chat - logService.info('No active session found or invalid, starting fresh chat.'); - startNewChat(); + if (sortedList.length > 0) { + const fallbackSessionId = sortedList[0].id; + const fullFallbackSession = await dbService.getSession(fallbackSessionId); + + if (fullFallbackSession) { + const rehydrated = rehydrateSessionFiles(fullFallbackSession); + setActiveMessages(rehydrated.messages); + setActiveSessionId(fallbackSessionId); + const draftFiles = fileDraftsRef.current[fallbackSessionId] || []; + setSelectedFiles(draftFiles); + logService.info(`Recovered latest session as active: ${fallbackSessionId}`); + } else { + logService.info('No recoverable active session found, starting fresh chat.'); + startNewChat(); + } + } else { + // Fallback: New Chat (truly first launch / empty database) + logService.info('No session history found, starting fresh chat.'); + startNewChat(); + } } } catch (error) { diff --git a/all-model-chat/hooks/chat/messages/useMessageActions.ts b/all-model-chat/apps/web/hooks/chat/messages/useMessageActions.ts similarity index 100% rename from all-model-chat/hooks/chat/messages/useMessageActions.ts rename to all-model-chat/apps/web/hooks/chat/messages/useMessageActions.ts diff --git a/all-model-chat/hooks/chat/messages/useTextToSpeechHandler.ts b/all-model-chat/apps/web/hooks/chat/messages/useTextToSpeechHandler.ts similarity index 100% rename from all-model-chat/hooks/chat/messages/useTextToSpeechHandler.ts rename to all-model-chat/apps/web/hooks/chat/messages/useTextToSpeechHandler.ts diff --git a/all-model-chat/hooks/chat/useAutoTitling.ts b/all-model-chat/apps/web/hooks/chat/useAutoTitling.ts similarity index 100% rename from all-model-chat/hooks/chat/useAutoTitling.ts rename to all-model-chat/apps/web/hooks/chat/useAutoTitling.ts diff --git a/all-model-chat/hooks/chat/useChat.ts b/all-model-chat/apps/web/hooks/chat/useChat.ts similarity index 85% rename from all-model-chat/hooks/chat/useChat.ts rename to all-model-chat/apps/web/hooks/chat/useChat.ts index 88b74cc7..93f01483 100644 --- a/all-model-chat/hooks/chat/useChat.ts +++ b/all-model-chat/apps/web/hooks/chat/useChat.ts @@ -1,6 +1,6 @@ import React, { useRef, useCallback } from 'react'; -import { AppSettings, UploadedFile } from '../../types'; +import { AppSettings, UploadedFile, ProjectContext } from '../../types'; import { useModels } from '../core/useModels'; import { useChatHistory } from './useChatHistory'; import { useFileHandling } from '../files/useFileHandling'; @@ -16,7 +16,7 @@ import { useChatEffects } from './useChatEffects'; import { useBackgroundKeepAlive } from '../core/useBackgroundKeepAlive'; export const useChat = (appSettings: AppSettings, setAppSettings: React.Dispatch>, language: 'en' | 'zh') => { - + const chatState = useChatState(appSettings); const { savedSessions, setSavedSessions, savedGroups, setSavedGroups, @@ -39,7 +39,10 @@ export const useChat = (appSettings: AppSettings, setAppSettings: React.Dispatch setCurrentChatSettings, updateAndPersistSessions, updateAndPersistGroups, fileDraftsRef, refreshSessions, - setSessionLoading + setSessionLoading, + // Agentic folder access + projectContext, setProjectContext, + projectContextReadState, setProjectContextReadState, } = chatState; // Optimize background performance when loading @@ -48,20 +51,19 @@ export const useChat = (appSettings: AppSettings, setAppSettings: React.Dispatch const sessionKeyMapRef = useRef>(new Map()); const { apiModels, isModelsLoading, modelsLoadingError, setApiModels } = useModels(); - const historyHandler = useChatHistory({ - appSettings, setSavedSessions, setSavedGroups, setActiveSessionId, setActiveMessages, // Pass setter - setEditingMessageId, setCommandedInput, setSelectedFiles, activeJobs, + appSettings, setSavedSessions, setSavedGroups, setActiveSessionId, setActiveMessages, + setEditingMessageId, setCommandedInput, setSelectedFiles, activeJobs, updateAndPersistSessions, activeChat, language, updateAndPersistGroups, userScrolledUp, selectedFiles, fileDraftsRef, activeSessionId }); - - const fileHandler = useFileHandling({ - appSettings, selectedFiles, setSelectedFiles, setAppFileError, - isAppProcessingFile, setIsAppProcessingFile, currentChatSettings, - setCurrentChatSettings + + const fileHandler = useFileHandling({ + appSettings, selectedFiles, setSelectedFiles, setAppFileError, + isAppProcessingFile, setIsAppProcessingFile, currentChatSettings, + setCurrentChatSettings }); - + const handleAddTempFile = useCallback((file: UploadedFile) => { setSelectedFiles(prev => [...prev, file]); }, [setSelectedFiles]); @@ -69,11 +71,27 @@ export const useChat = (appSettings: AppSettings, setAppSettings: React.Dispatch const handleRemoveTempFile = useCallback((id: string) => { setSelectedFiles(prev => prev.filter(f => f.id !== id)); }, [setSelectedFiles]); - - const dragDropHandler = useFileDragDrop({ + + // Handle agentic folder context creation + const handleProjectContextCreated = useCallback((context: ProjectContext) => { + setProjectContext(context); + setProjectContextReadState({ readFiles: new Set(), loadingFile: null }); + }, [setProjectContext, setProjectContextReadState]); + + const handleClearProjectContext = useCallback(() => { + setProjectContext(null); + setProjectContextReadState({ readFiles: new Set(), loadingFile: null }); + }, [setProjectContext, setProjectContextReadState]); + + // Use agentic mode when available (enabled by default for now) + const useAgenticMode = true; + + const dragDropHandler = useFileDragDrop({ onFilesDropped: fileHandler.handleProcessAndAddFiles, onAddTempFile: handleAddTempFile, - onRemoveTempFile: handleRemoveTempFile + onRemoveTempFile: handleRemoveTempFile, + onProjectContextCreated: handleProjectContextCreated, + useAgenticMode, }); const scenarioHandler = usePreloadedScenarios({ @@ -82,18 +100,19 @@ export const useChat = (appSettings: AppSettings, setAppSettings: React.Dispatch updateAndPersistSessions, setActiveSessionId, }); - + const scrollHandler = useChatScroll({ messages, userScrolledUp }); - - const messageHandler = useMessageHandler({ - appSettings, messages, isLoading, currentChatSettings, selectedFiles, - setSelectedFiles, editingMessageId, setEditingMessageId, setEditMode, setAppFileError, - aspectRatio, userScrolledUp, ttsMessageId, setTtsMessageId, activeSessionId, - setActiveSessionId, setCommandedInput, activeJobs, loadingSessionIds, - setLoadingSessionIds, updateAndPersistSessions, language, + + const messageHandler = useMessageHandler({ + appSettings, messages, isLoading, currentChatSettings, selectedFiles, + setSelectedFiles, editingMessageId, setEditingMessageId, setEditMode, setAppFileError, + aspectRatio, userScrolledUp, ttsMessageId, setTtsMessageId, activeSessionId, + setActiveSessionId, setCommandedInput, activeJobs, loadingSessionIds, + setLoadingSessionIds, updateAndPersistSessions, language, scrollContainerRef: scrollHandler.scrollContainerRef, sessionKeyMapRef, - setSessionLoading + setSessionLoading, + projectContext, // Enable agentic folder access }); useAutoTitling({ appSettings, savedSessions, updateAndPersistSessions, language, generatingTitleSessionIds, setGeneratingTitleSessionIds, sessionKeyMapRef }); @@ -169,17 +188,17 @@ export const useChat = (appSettings: AppSettings, setAppSettings: React.Dispatch imageSize, setImageSize, ttsMessageId, - + updateAndPersistSessions, updateAndPersistGroups, - + scrollContainerRef: scrollHandler.scrollContainerRef, setScrollContainerRef: scrollHandler.setScrollContainerRef, scrollNavVisibility: scrollHandler.scrollNavVisibility, onScrollContainerScroll: scrollHandler.handleScroll, scrollToPrevTurn: scrollHandler.scrollToPrevTurn, scrollToNextTurn: scrollHandler.scrollToNextTurn, - + loadChatSession, startNewChat, handleDeleteChatHistorySession, @@ -193,7 +212,7 @@ export const useChat = (appSettings: AppSettings, setAppSettings: React.Dispatch handleToggleGroupExpansion: historyHandler.handleToggleGroupExpansion, clearCacheAndReload: historyHandler.clearCacheAndReload, clearAllHistory: historyHandler.clearAllHistory, - + isAppDraggingOver: dragDropHandler.isAppDraggingOver, isProcessingDrop: dragDropHandler.isProcessingDrop, handleProcessAndAddFiles: fileHandler.handleProcessAndAddFiles, @@ -204,7 +223,7 @@ export const useChat = (appSettings: AppSettings, setAppSettings: React.Dispatch handleCancelFileUpload: fileHandler.handleCancelFileUpload, handleCancelUpload: fileHandler.handleCancelFileUpload, handleAddFileById: fileHandler.handleAddFileById, - + handleSendMessage: messageHandler.handleSendMessage, handleGenerateCanvas: messageHandler.handleGenerateCanvas, handleStopGenerating: messageHandler.handleStopGenerating, @@ -217,11 +236,11 @@ export const useChat = (appSettings: AppSettings, setAppSettings: React.Dispatch handleQuickTTS: messageHandler.handleQuickTTS, handleEditLastUserMessage: messageHandler.handleEditLastUserMessage, handleContinueGeneration: messageHandler.handleContinueGeneration, - + savedScenarios: scenarioHandler.savedScenarios, handleSaveAllScenarios: scenarioHandler.handleSaveAllScenarios, handleLoadPreloadedScenario: scenarioHandler.handleLoadPreloadedScenario, - + handleTranscribeAudio: chatActions.handleTranscribeAudio, setCurrentChatSettings, handleSelectModelInHeader: chatActions.handleSelectModelInHeader, @@ -235,5 +254,11 @@ export const useChat = (appSettings: AppSettings, setAppSettings: React.Dispatch handleUpdateMessageFile: chatActions.handleUpdateMessageFile, handleAddUserMessage: chatActions.handleAddUserMessage, handleLiveTranscript: chatActions.handleLiveTranscript, + + // Agentic folder access + projectContext, + projectContextReadState, + handleProjectContextCreated, + handleClearProjectContext, }; }; diff --git a/all-model-chat/hooks/chat/useChatActions.ts b/all-model-chat/apps/web/hooks/chat/useChatActions.ts similarity index 100% rename from all-model-chat/hooks/chat/useChatActions.ts rename to all-model-chat/apps/web/hooks/chat/useChatActions.ts diff --git a/all-model-chat/hooks/chat/useChatEffects.ts b/all-model-chat/apps/web/hooks/chat/useChatEffects.ts similarity index 100% rename from all-model-chat/hooks/chat/useChatEffects.ts rename to all-model-chat/apps/web/hooks/chat/useChatEffects.ts diff --git a/all-model-chat/hooks/chat/useChatHistory.ts b/all-model-chat/apps/web/hooks/chat/useChatHistory.ts similarity index 100% rename from all-model-chat/hooks/chat/useChatHistory.ts rename to all-model-chat/apps/web/hooks/chat/useChatHistory.ts diff --git a/all-model-chat/hooks/chat/useChatScroll.ts b/all-model-chat/apps/web/hooks/chat/useChatScroll.ts similarity index 100% rename from all-model-chat/hooks/chat/useChatScroll.ts rename to all-model-chat/apps/web/hooks/chat/useChatScroll.ts diff --git a/all-model-chat/hooks/chat/useChatState.ts b/all-model-chat/apps/web/hooks/chat/useChatState.ts similarity index 79% rename from all-model-chat/hooks/chat/useChatState.ts rename to all-model-chat/apps/web/hooks/chat/useChatState.ts index cf472568..6e11d001 100644 --- a/all-model-chat/hooks/chat/useChatState.ts +++ b/all-model-chat/apps/web/hooks/chat/useChatState.ts @@ -1,10 +1,11 @@ import { useState, useRef, useCallback, useMemo, useEffect } from 'react'; -import { AppSettings, ChatGroup, SavedChatSession, UploadedFile, ChatSettings as IndividualChatSettings, InputCommand, ChatMessage } from '../../types'; +import { AppSettings, ChatGroup, SavedChatSession, UploadedFile, ChatSettings as IndividualChatSettings, InputCommand, ChatMessage, ProjectContext, ProjectContextReadState } from '../../types'; import { DEFAULT_CHAT_SETTINGS, ACTIVE_CHAT_SESSION_ID_KEY } from '../../constants/appConstants'; import { dbService } from '../../utils/db'; import { logService, rehydrateSessionFiles } from '../../utils/appUtils'; import { useMultiTabSync } from '../core/useMultiTabSync'; +import { sanitizeSessionForStorage } from '../../utils/security/sensitiveData'; export const useChatState = (appSettings: AppSettings) => { // Session Metadata List (messages field is always empty [] in this array to save memory/renders) @@ -31,23 +32,46 @@ export const useChatState = (appSettings: AppSettings) => { const userScrolledUp = useRef(false); const fileDraftsRef = useRef>({}); + // Agentic folder access state + const [projectContext, setProjectContext] = useState(null); + const [projectContextReadState, setProjectContextReadState] = useState({ + readFiles: new Set(), + loadingFile: null, + }); + // Tracks session IDs that are generating *in this specific tab*. const localLoadingSessionIds = useRef(new Set()); // Refs to access latest state inside the heavy updater without adding dependencies const activeMessagesRef = useRef([]); const activeSessionIdRef = useRef(null); + const hasInitializedActiveSessionSync = useRef(false); useEffect(() => { activeMessagesRef.current = activeMessages; }, [activeMessages]); useEffect(() => { activeSessionIdRef.current = activeSessionId; }, [activeSessionId]); - // Sync active session ID to sessionStorage and URL + // Sync active session ID to sessionStorage, URL and IndexedDB useEffect(() => { + // Avoid wiping a previously persisted active session before initial history hydration finishes. + if (!hasInitializedActiveSessionSync.current) { + hasInitializedActiveSessionSync.current = true; + if (!activeSessionId) { + return; + } + } + if (activeSessionId) { try { sessionStorage.setItem(ACTIVE_CHAT_SESSION_ID_KEY, activeSessionId); - } catch (e) {} - + } catch (e) { + // Ignore storage errors + } + + dbService.setActiveSessionId(activeSessionId).catch(error => { + logService.error('Failed to persist active session ID', { error }); + }); + + // Sync URL: If the current URL doesn't match the active session, update it. const targetPath = `/chat/${activeSessionId}`; try { if (window.location.pathname !== targetPath) { @@ -59,11 +83,19 @@ export const useChatState = (appSettings: AppSettings) => { } else { try { sessionStorage.removeItem(ACTIVE_CHAT_SESSION_ID_KEY); - } catch (e) {} - + } catch (e) { + // Ignore storage errors + } + + dbService.setActiveSessionId(null).catch(error => { + logService.error('Failed to clear active session ID', { error }); + }); + + // If explicit "no session" (which usually means landing page or new chat pending), revert to root if not already + // Note: The app usually auto-creates a session ID for "New Chat", so this might run transiently. try { if (window.location.pathname !== '/' && !window.location.pathname.startsWith('/chat/')) { - window.history.pushState({}, '', '/'); + window.history.pushState({}, '', '/'); } } catch (e) { console.warn('Unable to update URL history:', e); @@ -169,7 +201,6 @@ export const useChatState = (appSettings: AppSettings) => { options: { persist?: boolean } = {} ) => { const { persist = true } = options; - setSavedSessions(prevMetadataSessions => { const currentActiveId = activeSessionIdRef.current; const currentActiveMsgs = activeMessagesRef.current; @@ -187,7 +218,7 @@ export const useChatState = (appSettings: AppSettings) => { const newFullSessions = updater(virtualFullSessions); // 3. Sort - newFullSessions.sort((a, b) => { + newFullSessions.sort((a, b) => { if (a.isPinned && !b.isPinned) return -1; if (!a.isPinned && b.isPinned) return 1; return b.timestamp - a.timestamp; @@ -207,48 +238,47 @@ export const useChatState = (appSettings: AppSettings) => { // 5. Persist if (persist) { - const updates: Promise[] = []; - const newSessionsMap = new Map(newFullSessions.map(s => [s.id, s])); - const modifiedSessionIds: string[] = []; - - // Save changed sessions - newFullSessions.forEach(session => { - const prevSession = virtualFullSessions.find(ps => ps.id === session.id); - if (prevSession !== session) { - updates.push(dbService.saveSession(session)); - modifiedSessionIds.push(session.id); - } - }); + const updates: Promise[] = []; + const newSessionsMap = new Map(newFullSessions.map(s => [s.id, s])); + const modifiedSessionIds: string[] = []; - // Delete removed sessions - prevMetadataSessions.forEach(session => { - if (!newSessionsMap.has(session.id)) { - updates.push(dbService.deleteSession(session.id)); - } - }); + // Save changed sessions + newFullSessions.forEach(session => { + const prevSession = virtualFullSessions.find(ps => ps.id === session.id); + if (prevSession !== session) { + updates.push(dbService.saveSession(sanitizeSessionForStorage(session))); + modifiedSessionIds.push(session.id); + } + }); - if (updates.length > 0) { - Promise.all(updates).then(() => { + // Delete removed sessions + prevMetadataSessions.forEach(session => { + if (!newSessionsMap.has(session.id)) { + updates.push(dbService.deleteSession(session.id)); + } + }); + + if (updates.length > 0) { + Promise.all(updates).then(() => { if (modifiedSessionIds.length === 1) { broadcast({ type: 'SESSION_CONTENT_UPDATED', sessionId: modifiedSessionIds[0] }); } else { broadcast({ type: 'SESSIONS_UPDATED' }); } - }).catch(e => logService.error('Failed to persist session updates', { error: e })); - } + }).catch(e => logService.error('Failed to persist session updates', { error: e })); + } } // 6. Return Metadata Only for `savedSessions` state // Strip messages to keep the list lightweight return newFullSessions.map(s => { - // Optimized strip: avoid spread if messages is already empty (mostly true for inactive) if (s.messages && s.messages.length === 0) return s; const { messages, ...rest } = s; return { ...rest, messages: [] }; }); }); }, [broadcast]); - + const updateAndPersistGroups = useCallback(async (updater: (prev: ChatGroup[]) => ChatGroup[]) => { setSavedGroups(prevGroups => { const newGroups = updater(prevGroups); @@ -271,7 +301,7 @@ export const useChatState = (appSettings: AppSettings) => { // Fallback/Default settings const currentChatSettings = useMemo(() => activeChat?.settings || DEFAULT_CHAT_SETTINGS, [activeChat]); const isLoading = useMemo(() => loadingSessionIds.has(activeSessionId ?? ''), [loadingSessionIds, activeSessionId]); - + const setCurrentChatSettings = useCallback((updater: (prevSettings: IndividualChatSettings) => IndividualChatSettings) => { if (!activeSessionId) return; updateAndPersistSessions(prevSessions => @@ -313,6 +343,9 @@ export const useChatState = (appSettings: AppSettings) => { fileDraftsRef, refreshSessions, refreshGroups, - setSessionLoading + setSessionLoading, + // Agentic folder access + projectContext, setProjectContext, + projectContextReadState, setProjectContextReadState, }; }; diff --git a/all-model-chat/hooks/chat/useSuggestions.ts b/all-model-chat/apps/web/hooks/chat/useSuggestions.ts similarity index 100% rename from all-model-chat/hooks/chat/useSuggestions.ts rename to all-model-chat/apps/web/hooks/chat/useSuggestions.ts diff --git a/all-model-chat/hooks/core/useAppEvents.ts b/all-model-chat/apps/web/hooks/core/useAppEvents.ts similarity index 100% rename from all-model-chat/hooks/core/useAppEvents.ts rename to all-model-chat/apps/web/hooks/core/useAppEvents.ts diff --git a/all-model-chat/hooks/core/useAppSettings.ts b/all-model-chat/apps/web/hooks/core/useAppSettings.ts similarity index 86% rename from all-model-chat/hooks/core/useAppSettings.ts rename to all-model-chat/apps/web/hooks/core/useAppSettings.ts index ff5d404b..e684790e 100644 --- a/all-model-chat/hooks/core/useAppSettings.ts +++ b/all-model-chat/apps/web/hooks/core/useAppSettings.ts @@ -6,6 +6,8 @@ import { AVAILABLE_THEMES, DEFAULT_THEME_ID } from '../../constants/themeConstan import { applyThemeToDocument, logService } from '../../utils/appUtils'; import { dbService } from '../../utils/db'; import { useMultiTabSync } from './useMultiTabSync'; +import { runSensitiveDataCleanupMigration } from '../../utils/security/legacy_cleanup'; +import { sanitizeAppSettingsForStorage } from '../../utils/security/sensitiveData'; export const useAppSettings = () => { const [appSettings, setAppSettingsState] = useState(DEFAULT_APP_SETTINGS); @@ -13,13 +15,15 @@ export const useAppSettings = () => { const loadSettings = useCallback(async () => { try { + await runSensitiveDataCleanupMigration(); const storedSettings = await dbService.getAppSettings(); if (storedSettings) { - const newSettings = { ...DEFAULT_APP_SETTINGS, ...storedSettings }; + const sanitizedStoredSettings = sanitizeAppSettingsForStorage(storedSettings); + const newSettings = { ...DEFAULT_APP_SETTINGS, ...sanitizedStoredSettings }; - if (storedSettings.filesApiConfig) { + if (sanitizedStoredSettings.filesApiConfig) { // Ensure new keys are present if structure updated - newSettings.filesApiConfig = { ...DEFAULT_FILES_API_CONFIG, ...storedSettings.filesApiConfig }; + newSettings.filesApiConfig = { ...DEFAULT_FILES_API_CONFIG, ...sanitizedStoredSettings.filesApiConfig }; } setAppSettingsState(newSettings); @@ -73,10 +77,11 @@ export const useAppSettings = () => { const setAppSettings = useCallback((newSettings: AppSettings | ((prev: AppSettings) => AppSettings)) => { setAppSettingsState(prev => { const next = typeof newSettings === 'function' ? newSettings(prev) : newSettings; + const sanitizedForStorage = sanitizeAppSettingsForStorage(next); // Persist and Broadcast if (isSettingsLoaded) { - dbService.setAppSettings(next) + dbService.setAppSettings(sanitizedForStorage) .then(() => broadcast({ type: 'SETTINGS_UPDATED' })) .catch(e => logService.error("Failed to save settings", { error: e })); } diff --git a/all-model-chat/hooks/core/useAppUI.ts b/all-model-chat/apps/web/hooks/core/useAppUI.ts similarity index 100% rename from all-model-chat/hooks/core/useAppUI.ts rename to all-model-chat/apps/web/hooks/core/useAppUI.ts diff --git a/all-model-chat/hooks/core/useBackgroundKeepAlive.ts b/all-model-chat/apps/web/hooks/core/useBackgroundKeepAlive.ts similarity index 100% rename from all-model-chat/hooks/core/useBackgroundKeepAlive.ts rename to all-model-chat/apps/web/hooks/core/useBackgroundKeepAlive.ts diff --git a/all-model-chat/hooks/core/useModels.ts b/all-model-chat/apps/web/hooks/core/useModels.ts similarity index 100% rename from all-model-chat/hooks/core/useModels.ts rename to all-model-chat/apps/web/hooks/core/useModels.ts diff --git a/all-model-chat/hooks/core/useMultiTabSync.ts b/all-model-chat/apps/web/hooks/core/useMultiTabSync.ts similarity index 100% rename from all-model-chat/hooks/core/useMultiTabSync.ts rename to all-model-chat/apps/web/hooks/core/useMultiTabSync.ts diff --git a/all-model-chat/hooks/core/usePictureInPicture.ts b/all-model-chat/apps/web/hooks/core/usePictureInPicture.ts similarity index 100% rename from all-model-chat/hooks/core/usePictureInPicture.ts rename to all-model-chat/apps/web/hooks/core/usePictureInPicture.ts diff --git a/all-model-chat/hooks/core/useRecorder.ts b/all-model-chat/apps/web/hooks/core/useRecorder.ts similarity index 100% rename from all-model-chat/hooks/core/useRecorder.ts rename to all-model-chat/apps/web/hooks/core/useRecorder.ts diff --git a/all-model-chat/hooks/data-management/useChatSessionExport.ts b/all-model-chat/apps/web/hooks/data-management/useChatSessionExport.ts similarity index 100% rename from all-model-chat/hooks/data-management/useChatSessionExport.ts rename to all-model-chat/apps/web/hooks/data-management/useChatSessionExport.ts diff --git a/all-model-chat/hooks/data-management/useDataExport.ts b/all-model-chat/apps/web/hooks/data-management/useDataExport.ts similarity index 91% rename from all-model-chat/hooks/data-management/useDataExport.ts rename to all-model-chat/apps/web/hooks/data-management/useDataExport.ts index da35778a..f3f4e622 100644 --- a/all-model-chat/hooks/data-management/useDataExport.ts +++ b/all-model-chat/apps/web/hooks/data-management/useDataExport.ts @@ -3,6 +3,7 @@ import { useCallback } from 'react'; import { AppSettings, SavedChatSession, SavedScenario, ChatGroup } from '../../types'; import { logService, sanitizeSessionForExport } from '../../utils/appUtils'; import { triggerDownload } from '../../utils/exportUtils'; +import { sanitizeAppSettingsForStorage } from '../../utils/security/sensitiveData'; interface UseDataExportProps { appSettings: AppSettings; @@ -23,7 +24,11 @@ export const useDataExport = ({ const handleExportSettings = useCallback(() => { logService.info(`Exporting settings.`); try { - const dataToExport = { type: 'AllModelChat-Settings', version: 1, settings: appSettings }; + const dataToExport = { + type: 'AllModelChat-Settings', + version: 1, + settings: sanitizeAppSettingsForStorage(appSettings), + }; const jsonString = JSON.stringify(dataToExport, null, 2); const blob = new Blob([jsonString], { type: 'application/json' }); const date = new Date().toISOString().slice(0, 10); diff --git a/all-model-chat/hooks/data-management/useDataImport.ts b/all-model-chat/apps/web/hooks/data-management/useDataImport.ts similarity index 85% rename from all-model-chat/hooks/data-management/useDataImport.ts rename to all-model-chat/apps/web/hooks/data-management/useDataImport.ts index 045df5bb..35ad3fe9 100644 --- a/all-model-chat/hooks/data-management/useDataImport.ts +++ b/all-model-chat/apps/web/hooks/data-management/useDataImport.ts @@ -3,6 +3,7 @@ import { useCallback, Dispatch, SetStateAction } from 'react'; import { AppSettings, SavedChatSession, SavedScenario, ChatGroup } from '../../types'; import { DEFAULT_APP_SETTINGS } from '../../constants/appConstants'; import { logService } from '../../utils/appUtils'; +import { sanitizeAppSettingsForStorage, sanitizeSessionForStorage } from '../../utils/security/sensitiveData'; type SessionsUpdater = (updater: (prev: SavedChatSession[]) => SavedChatSession[]) => void; type GroupsUpdater = (updater: (prev: ChatGroup[]) => ChatGroup[]) => void; @@ -15,6 +16,13 @@ interface UseDataImportProps { t: (key: string) => string; } +const isImportTypeCompatible = (importedValue: unknown, defaultValue: unknown): boolean => { + if (defaultValue === null) { + return importedValue === null; + } + return typeof importedValue === typeof defaultValue; +}; + export const useDataImport = ({ setAppSettings, updateAndPersistSessions, @@ -22,7 +30,6 @@ export const useDataImport = ({ handleSaveAllScenarios, t }: UseDataImportProps) => { - const handleImportFile = useCallback((file: File, expectedType: string, onValid: (data: any) => void) => { logService.info(`Importing ${expectedType} from file: ${file.name}`); const reader = new FileReader(); @@ -55,14 +62,14 @@ export const useDataImport = ({ if (Object.prototype.hasOwnProperty.call(importedSettings, key)) { const importedValue = importedSettings[key]; const defaultValue = DEFAULT_APP_SETTINGS[key]; - if (typeof importedValue === typeof defaultValue || (['apiKey', 'apiProxyUrl', 'lockedApiKey'].includes(key) && (typeof importedValue === 'string' || importedValue === null))) { + if (isImportTypeCompatible(importedValue, defaultValue)) { (newSettings as any)[key] = importedValue; } else { logService.warn(`Type mismatch for setting "${key}" during import. Using default.`); } } } - setAppSettings(newSettings); + setAppSettings(sanitizeAppSettingsForStorage(newSettings)); alert(t('settingsImport_success')); }); }, [handleImportFile, setAppSettings, t]); @@ -70,9 +77,12 @@ export const useDataImport = ({ const handleImportHistory = useCallback((file: File) => { handleImportFile(file, 'AllModelChat-History', (data) => { if (data.history && Array.isArray(data.history)) { + const sanitizedImportedSessions = data.history.map((session: SavedChatSession) => + sanitizeSessionForStorage(session) + ); updateAndPersistSessions((prev) => { const existingIds = new Set(prev.map(s => s.id)); - const newSessions = data.history.filter((s: SavedChatSession) => !existingIds.has(s.id)); + const newSessions = sanitizedImportedSessions.filter((s: SavedChatSession) => !existingIds.has(s.id)); return [...prev, ...newSessions]; }); diff --git a/all-model-chat/hooks/features/useScenarioManager.ts b/all-model-chat/apps/web/hooks/features/useScenarioManager.ts similarity index 100% rename from all-model-chat/hooks/features/useScenarioManager.ts rename to all-model-chat/apps/web/hooks/features/useScenarioManager.ts diff --git a/all-model-chat/hooks/features/useSettingsLogic.ts b/all-model-chat/apps/web/hooks/features/useSettingsLogic.ts similarity index 57% rename from all-model-chat/hooks/features/useSettingsLogic.ts rename to all-model-chat/apps/web/hooks/features/useSettingsLogic.ts index 8708c108..0dc5b038 100644 --- a/all-model-chat/hooks/features/useSettingsLogic.ts +++ b/all-model-chat/apps/web/hooks/features/useSettingsLogic.ts @@ -1,5 +1,5 @@ -import { useState, useEffect, useRef, useLayoutEffect, useMemo } from 'react'; +import { useState, useEffect, useRef, useLayoutEffect, useMemo, useCallback } from 'react'; import { AppSettings } from '../../types'; import { DEFAULT_APP_SETTINGS } from '../../constants/appConstants'; import { translations, logService, cacheModelSettings, getCachedModelSettings, adjustThinkingBudget } from '../../utils/appUtils'; @@ -8,6 +8,9 @@ import { IconInterface, IconModel, IconApiKey, IconData, IconAbout, IconKeyboard export type SettingsTab = 'interface' | 'model' | 'account' | 'data' | 'shortcuts' | 'about'; +// Tabs that require confirmation to save +const CONFIRM_REQUIRED_TABS: SettingsTab[] = ['model', 'interface', 'account']; + const SETTINGS_TAB_STORAGE_KEY = 'chatSettingsLastTab'; interface UseSettingsLogicProps { @@ -46,6 +49,25 @@ export const useSettingsLogic = ({ return 'model'; }); + // Pending settings state - stores uncommitted changes + const [pendingSettings, setPendingSettings] = useState(currentSettings); + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + + // Sync pending settings when currentSettings changes (e.g., from external updates) + useEffect(() => { + if (!hasUnsavedChanges) { + setPendingSettings(currentSettings); + } + }, [currentSettings, hasUnsavedChanges]); + + // Reset pending state when modal opens + useEffect(() => { + if (isOpen) { + setPendingSettings(currentSettings); + setHasUnsavedChanges(false); + } + }, [isOpen, currentSettings]); + const [confirmConfig, setConfirmConfig] = useState<{ isOpen: boolean; title: string; @@ -97,7 +119,11 @@ export const useSettingsLogic = ({ isOpen: true, title: t('settingsReset'), message: t('settingsReset_confirm'), - onConfirm: () => onSave(DEFAULT_APP_SETTINGS), + onConfirm: () => { + onSave(DEFAULT_APP_SETTINGS); + setPendingSettings(DEFAULT_APP_SETTINGS); + setHasUnsavedChanges(false); + }, isDanger: true, confirmLabel: t('settingsReset') }); @@ -147,37 +173,107 @@ export const useSettingsLogic = ({ }); }; + // Check if current tab requires confirmation + const requiresConfirmation = CONFIRM_REQUIRED_TABS.includes(activeTab); + + // Update pending settings (for tabs that require confirmation) const updateSetting = (key: K, value: AppSettings[K]) => { - onSave({ ...currentSettings, [key]: value }); + if (requiresConfirmation) { + // Update pending settings, not actual settings + setPendingSettings(prev => ({ ...prev, [key]: value })); + setHasUnsavedChanges(true); + } else { + // For other tabs (data, shortcuts, about), save immediately + onSave({ ...currentSettings, [key]: value }); + } }; + // Save pending changes + const savePendingChanges = useCallback(() => { + onSave(pendingSettings); + + // Sync thinking settings to cache + if (pendingSettings.modelId && ( + pendingSettings.thinkingBudget !== currentSettings.thinkingBudget || + pendingSettings.thinkingLevel !== currentSettings.thinkingLevel + )) { + const currentCache = getCachedModelSettings(pendingSettings.modelId) || {}; + cacheModelSettings(pendingSettings.modelId, { + ...currentCache, + thinkingBudget: pendingSettings.thinkingBudget, + thinkingLevel: pendingSettings.thinkingLevel + }); + } + + setHasUnsavedChanges(false); + }, [pendingSettings, currentSettings, onSave]); + + // Discard pending changes + const discardPendingChanges = useCallback(() => { + setPendingSettings(currentSettings); + setHasUnsavedChanges(false); + }, [currentSettings]); + + // Handle close with unsaved changes warning + const handleClose = useCallback(() => { + if (hasUnsavedChanges) { + setConfirmConfig({ + isOpen: true, + title: t('settingsUnsavedChanges') || 'Unsaved Changes', + message: t('settingsUnsavedChanges_confirm') || 'You have unsaved changes. Do you want to save them before closing?', + onConfirm: () => { + savePendingChanges(); + onClose(); + }, + isDanger: false, + confirmLabel: t('save') || 'Save' + }); + } else { + onClose(); + } + }, [hasUnsavedChanges, savePendingChanges, onClose, t]); + + // Handle discard and close + const handleDiscardAndClose = useCallback(() => { + discardPendingChanges(); + onClose(); + }, [discardPendingChanges, onClose]); + const handleModelChange = (newModelId: string) => { - // 1. Cache current model settings - if (currentSettings.modelId) { - cacheModelSettings(currentSettings.modelId, { - mediaResolution: currentSettings.mediaResolution, - thinkingBudget: currentSettings.thinkingBudget, - thinkingLevel: currentSettings.thinkingLevel + // 1. Cache current model settings (from pending if we have unsaved changes) + const settingsToCache = hasUnsavedChanges ? pendingSettings : currentSettings; + if (settingsToCache.modelId) { + cacheModelSettings(settingsToCache.modelId, { + mediaResolution: settingsToCache.mediaResolution, + thinkingBudget: settingsToCache.thinkingBudget, + thinkingLevel: settingsToCache.thinkingLevel }); } // 2. Load cached settings for new model const cached = getCachedModelSettings(newModelId); - let newThinkingBudget = cached?.thinkingBudget ?? currentSettings.thinkingBudget; - const newThinkingLevel = cached?.thinkingLevel ?? currentSettings.thinkingLevel; - const newMediaResolution = cached?.mediaResolution ?? currentSettings.mediaResolution ?? MediaResolution.MEDIA_RESOLUTION_UNSPECIFIED; + let newThinkingBudget = cached?.thinkingBudget ?? settingsToCache.thinkingBudget; + const newThinkingLevel = cached?.thinkingLevel ?? settingsToCache.thinkingLevel; + const newMediaResolution = cached?.mediaResolution ?? settingsToCache.mediaResolution ?? MediaResolution.MEDIA_RESOLUTION_UNSPECIFIED; // 3. Apply defaults/clamping logic using shared helper newThinkingBudget = adjustThinkingBudget(newModelId, newThinkingBudget); - onSave({ - ...currentSettings, + const newSettings = { + ...settingsToCache, modelId: newModelId, thinkingBudget: newThinkingBudget, thinkingLevel: newThinkingLevel, mediaResolution: newMediaResolution, - }); + }; + + if (requiresConfirmation) { + setPendingSettings(newSettings); + setHasUnsavedChanges(true); + } else { + onSave(newSettings); + } }; const tabs = useMemo(() => [ @@ -205,6 +301,14 @@ export const useSettingsLogic = ({ handleRequestImportHistory, updateSetting, handleModelChange, - tabs + tabs, + // New exports for pending state management + pendingSettings, + hasUnsavedChanges, + savePendingChanges, + discardPendingChanges, + handleClose, + handleDiscardAndClose, + requiresConfirmation }; }; diff --git a/all-model-chat/hooks/features/useTokenCountLogic.ts b/all-model-chat/apps/web/hooks/features/useTokenCountLogic.ts similarity index 100% rename from all-model-chat/hooks/features/useTokenCountLogic.ts rename to all-model-chat/apps/web/hooks/features/useTokenCountLogic.ts diff --git a/all-model-chat/hooks/file-upload/uploadFileItem.ts b/all-model-chat/apps/web/hooks/file-upload/uploadFileItem.ts similarity index 100% rename from all-model-chat/hooks/file-upload/uploadFileItem.ts rename to all-model-chat/apps/web/hooks/file-upload/uploadFileItem.ts diff --git a/all-model-chat/hooks/file-upload/useFileIdAdder.ts b/all-model-chat/apps/web/hooks/file-upload/useFileIdAdder.ts similarity index 100% rename from all-model-chat/hooks/file-upload/useFileIdAdder.ts rename to all-model-chat/apps/web/hooks/file-upload/useFileIdAdder.ts diff --git a/all-model-chat/hooks/file-upload/useFilePreProcessing.ts b/all-model-chat/apps/web/hooks/file-upload/useFilePreProcessing.ts similarity index 100% rename from all-model-chat/hooks/file-upload/useFilePreProcessing.ts rename to all-model-chat/apps/web/hooks/file-upload/useFilePreProcessing.ts diff --git a/all-model-chat/hooks/file-upload/useFileUploader.ts b/all-model-chat/apps/web/hooks/file-upload/useFileUploader.ts similarity index 100% rename from all-model-chat/hooks/file-upload/useFileUploader.ts rename to all-model-chat/apps/web/hooks/file-upload/useFileUploader.ts diff --git a/all-model-chat/hooks/file-upload/utils.ts b/all-model-chat/apps/web/hooks/file-upload/utils.ts similarity index 100% rename from all-model-chat/hooks/file-upload/utils.ts rename to all-model-chat/apps/web/hooks/file-upload/utils.ts diff --git a/all-model-chat/hooks/files/useFileDragDrop.ts b/all-model-chat/apps/web/hooks/files/useFileDragDrop.ts similarity index 81% rename from all-model-chat/hooks/files/useFileDragDrop.ts rename to all-model-chat/apps/web/hooks/files/useFileDragDrop.ts index 3cc4c6b1..96351acc 100644 --- a/all-model-chat/hooks/files/useFileDragDrop.ts +++ b/all-model-chat/apps/web/hooks/files/useFileDragDrop.ts @@ -1,16 +1,20 @@ import React, { useState, useCallback } from 'react'; -import { generateFolderContext } from '../../utils/folderImportUtils'; -import { UploadedFile } from '../../types'; +import { generateFolderContext, buildProjectContext } from '../../utils/folderImportUtils'; +import { UploadedFile, ProjectContext } from '../../types'; import { generateUniqueId } from '../../utils/appUtils'; interface UseFileDragDropProps { onFilesDropped: (files: FileList | File[]) => Promise; onAddTempFile: (file: UploadedFile) => void; onRemoveTempFile: (id: string) => void; + /** Callback when folder is dropped in agentic mode - receives ProjectContext instead of merged file */ + onProjectContextCreated?: (context: ProjectContext) => void; + /** If true, use agentic mode (lazy file loading) instead of context stuffing */ + useAgenticMode?: boolean; } -export const useFileDragDrop = ({ onFilesDropped, onAddTempFile, onRemoveTempFile }: UseFileDragDropProps) => { +export const useFileDragDrop = ({ onFilesDropped, onAddTempFile, onRemoveTempFile, onProjectContextCreated, useAgenticMode = false }: UseFileDragDropProps) => { const [isAppDraggingOver, setIsAppDraggingOver] = useState(false); const [isProcessingDrop, setIsProcessingDrop] = useState(false); @@ -56,7 +60,7 @@ export const useFileDragDrop = ({ onFilesDropped, onAddTempFile, onRemoveTempFil } else if (entry.isDirectory) { const dirReader = entry.createReader(); const allEntries: any[] = []; - + const readEntries = async (): Promise => { return new Promise((resolve, reject) => { dirReader.readEntries((entries: any[]) => { @@ -74,7 +78,7 @@ export const useFileDragDrop = ({ onFilesDropped, onAddTempFile, onRemoveTempFil } catch (e) { console.warn('Error reading directory entries during drop', e); } - + const filesArrays = await Promise.all(allEntries.map(child => scanEntry(child, path + entry.name + '/'))); return filesArrays.flat(); } @@ -121,16 +125,22 @@ export const useFileDragDrop = ({ onFilesDropped, onAddTempFile, onRemoveTempFil .filter(item => item.kind === 'file') .map(item => (item as any).webkitGetAsEntry()) .filter(Boolean); - + const filesArrays = await Promise.all(entries.map(entry => scanEntry(entry))); const flatFilesWithPath = filesArrays.flat(); - + if (flatFilesWithPath.length > 0) { - // Convert list of file-path objects to a single text context file - const contextFile = await generateFolderContext(flatFilesWithPath); - await onFilesDropped([contextFile]); + if (useAgenticMode && onProjectContextCreated) { + // Agentic mode: Build project context without reading file contents + const projectContext = buildProjectContext(flatFilesWithPath); + onProjectContextCreated(projectContext); + } else { + // Legacy mode: Convert to a single text context file with all contents + const contextFile = await generateFolderContext(flatFilesWithPath); + await onFilesDropped([contextFile]); + } } - + onRemoveTempFile(tempId); } else { // Standard file drop @@ -144,7 +154,7 @@ export const useFileDragDrop = ({ onFilesDropped, onAddTempFile, onRemoveTempFil } finally { setIsProcessingDrop(false); } - }, [onFilesDropped, onAddTempFile, onRemoveTempFile]); + }, [onFilesDropped, onAddTempFile, onRemoveTempFile, onProjectContextCreated, useAgenticMode]); return { isAppDraggingOver, diff --git a/all-model-chat/hooks/files/useFileHandling.ts b/all-model-chat/apps/web/hooks/files/useFileHandling.ts similarity index 100% rename from all-model-chat/hooks/files/useFileHandling.ts rename to all-model-chat/apps/web/hooks/files/useFileHandling.ts diff --git a/all-model-chat/hooks/files/useFilePolling.ts b/all-model-chat/apps/web/hooks/files/useFilePolling.ts similarity index 100% rename from all-model-chat/hooks/files/useFilePolling.ts rename to all-model-chat/apps/web/hooks/files/useFilePolling.ts diff --git a/all-model-chat/hooks/files/useFileUpload.ts b/all-model-chat/apps/web/hooks/files/useFileUpload.ts similarity index 100% rename from all-model-chat/hooks/files/useFileUpload.ts rename to all-model-chat/apps/web/hooks/files/useFileUpload.ts diff --git a/all-model-chat/hooks/live-api/useLiveAudio.ts b/all-model-chat/apps/web/hooks/live-api/useLiveAudio.ts similarity index 100% rename from all-model-chat/hooks/live-api/useLiveAudio.ts rename to all-model-chat/apps/web/hooks/live-api/useLiveAudio.ts diff --git a/all-model-chat/hooks/live-api/useLiveConfig.ts b/all-model-chat/apps/web/hooks/live-api/useLiveConfig.ts similarity index 100% rename from all-model-chat/hooks/live-api/useLiveConfig.ts rename to all-model-chat/apps/web/hooks/live-api/useLiveConfig.ts diff --git a/all-model-chat/hooks/live-api/useLiveConnection.ts b/all-model-chat/apps/web/hooks/live-api/useLiveConnection.ts similarity index 100% rename from all-model-chat/hooks/live-api/useLiveConnection.ts rename to all-model-chat/apps/web/hooks/live-api/useLiveConnection.ts diff --git a/all-model-chat/hooks/live-api/useLiveFrameCapture.ts b/all-model-chat/apps/web/hooks/live-api/useLiveFrameCapture.ts similarity index 100% rename from all-model-chat/hooks/live-api/useLiveFrameCapture.ts rename to all-model-chat/apps/web/hooks/live-api/useLiveFrameCapture.ts diff --git a/all-model-chat/hooks/live-api/useLiveMessageProcessing.ts b/all-model-chat/apps/web/hooks/live-api/useLiveMessageProcessing.ts similarity index 100% rename from all-model-chat/hooks/live-api/useLiveMessageProcessing.ts rename to all-model-chat/apps/web/hooks/live-api/useLiveMessageProcessing.ts diff --git a/all-model-chat/hooks/live-api/useLiveTools.ts b/all-model-chat/apps/web/hooks/live-api/useLiveTools.ts similarity index 100% rename from all-model-chat/hooks/live-api/useLiveTools.ts rename to all-model-chat/apps/web/hooks/live-api/useLiveTools.ts diff --git a/all-model-chat/hooks/live-api/useLiveVideo.ts b/all-model-chat/apps/web/hooks/live-api/useLiveVideo.ts similarity index 100% rename from all-model-chat/hooks/live-api/useLiveVideo.ts rename to all-model-chat/apps/web/hooks/live-api/useLiveVideo.ts diff --git a/all-model-chat/apps/web/hooks/message-sender/standard/useApiInteraction.ts b/all-model-chat/apps/web/hooks/message-sender/standard/useApiInteraction.ts new file mode 100644 index 00000000..33b3c584 --- /dev/null +++ b/all-model-chat/apps/web/hooks/message-sender/standard/useApiInteraction.ts @@ -0,0 +1,281 @@ + +import React, { useCallback, Dispatch, SetStateAction } from 'react'; +import { AppSettings, ChatMessage, ChatSettings as IndividualChatSettings, UploadedFile, ProjectContext } from '../../../types'; +import { createChatHistoryForApi, isGemini3Model, logService } from '../../../utils/appUtils'; +import { buildGenerationConfig } from '../../../services/api/baseApi'; +import { geminiServiceInstance } from '../../../services/geminiService'; +import { isLikelyHtml } from '../../../utils/codeUtils'; +import { GetStreamHandlers } from '../types'; +import { ContentPart } from '../../../types/chat'; +import { generateProjectContextSystemPrompt } from '../../useFolderToolExecutor'; +import { readProjectFile } from '../../../utils/folderImportUtils'; + +interface UseApiInteractionProps { + appSettings: AppSettings; + messages: ChatMessage[]; + getStreamHandlers: GetStreamHandlers; + handleGenerateCanvas: (sourceMessageId: string, content: string) => Promise; + setSessionLoading: (sessionId: string, isLoading: boolean) => void; + activeJobs: React.MutableRefObject>; + /** Active project context for agentic folder access */ + projectContext?: ProjectContext | null; + onAutoContinue?: (params: { generationId: string; activeModelId: string; effectiveEditingId: string | null }) => void; +} + +export const useApiInteraction = ({ + appSettings, + messages, + getStreamHandlers, + handleGenerateCanvas, + setSessionLoading, + activeJobs, + projectContext, + onAutoContinue, +}: UseApiInteractionProps) => { + + const performApiCall = useCallback(async (params: { + finalSessionId: string; + generationId: string; + generationStartTime: Date; + keyToUse: string; + activeModelId: string; + promptParts: ContentPart[]; + effectiveEditingId: string | null; + isContinueMode: boolean; + isRawMode: boolean; + sessionToUpdate: IndividualChatSettings; + aspectRatio: string; + imageSize: string | undefined; + newAbortController: AbortController; + textToUse: string; + enrichedFiles: UploadedFile[]; + }) => { + const { + finalSessionId, generationId, generationStartTime, keyToUse, activeModelId, + promptParts, effectiveEditingId, isContinueMode, isRawMode, + sessionToUpdate, aspectRatio, imageSize, newAbortController, + textToUse, enrichedFiles + } = params; + + let baseMessagesForApi: ChatMessage[] = messages; + + if (effectiveEditingId) { + const index = messages.findIndex(m => m.id === effectiveEditingId); + if (index !== -1) { + baseMessagesForApi = messages.slice(0, index); + } + } + + let finalRole: 'user' | 'model' = 'user'; + let finalParts = promptParts; + + if (isContinueMode) { + finalRole = 'model'; + const targetMsg = messages.find(m => m.id === effectiveEditingId); + const currentContent = targetMsg?.content || ''; + const isG3 = isGemini3Model(activeModelId); + + let prefillContent = currentContent; + if (!prefillContent.trim()) { + prefillContent = isG3 ? "I have finished reasoning" : " "; + } + finalParts = [{ text: prefillContent }]; + + } else if (isRawMode) { + const tempUserMsg: ChatMessage = { + id: 'temp-raw-user', + role: 'user', + content: textToUse.trim(), + files: enrichedFiles, + timestamp: new Date() + }; + baseMessagesForApi = [...baseMessagesForApi, tempUserMsg]; + + finalRole = 'model'; + finalParts = [{ text: '' }]; + + } else if (promptParts.length === 0) { + setSessionLoading(finalSessionId, false); + activeJobs.current.delete(generationId); + return; + } + + const shouldStripThinking = sessionToUpdate.hideThinkingInContext ?? appSettings.hideThinkingInContext; + const historyForChat = await createChatHistoryForApi(baseMessagesForApi, shouldStripThinking); + + // Prepare system instruction - inject project context if available + let effectiveSystemInstruction = sessionToUpdate.systemInstruction; + if (projectContext) { + const projectPromptPrefix = generateProjectContextSystemPrompt(projectContext); + effectiveSystemInstruction = projectPromptPrefix + (effectiveSystemInstruction ? `\n\n${effectiveSystemInstruction}` : ''); + } + + const config = buildGenerationConfig( + activeModelId, + effectiveSystemInstruction, + { temperature: sessionToUpdate.temperature, topP: sessionToUpdate.topP }, + sessionToUpdate.showThoughts, + sessionToUpdate.thinkingBudget, + !!sessionToUpdate.isGoogleSearchEnabled, + !!sessionToUpdate.isCodeExecutionEnabled, + !!sessionToUpdate.isUrlContextEnabled, + sessionToUpdate.thinkingLevel, + aspectRatio, + sessionToUpdate.isDeepSearchEnabled, + imageSize, + sessionToUpdate.safetySettings, + sessionToUpdate.mediaResolution, + projectContext?.fileTree, // Pass file tree to enable read_file tool + ); + + const shouldAutoContinueOnEmpty = !isContinueMode && activeModelId.includes('gemini-3') && activeModelId.includes('flash'); + + const { streamOnError, streamOnComplete, streamOnPart, onThoughtChunk } = getStreamHandlers( + finalSessionId, + generationId, + newAbortController, + generationStartTime, + sessionToUpdate, + { + onSuccess: (msgId, content) => { + if (!isContinueMode && appSettings.autoCanvasVisualization && content && content.length > 50 && !isLikelyHtml(content)) { + const trimmed = content.trim(); + if (trimmed.startsWith('```') && trimmed.endsWith('```')) return; + logService.info("Auto-triggering Canvas visualization for message", { msgId }); + handleGenerateCanvas(msgId, content); + } + }, + onEmptyResponse: shouldAutoContinueOnEmpty + ? (msgId) => onAutoContinue?.({ generationId: msgId, activeModelId, effectiveEditingId }) + : undefined, + suppressEmptyResponseError: shouldAutoContinueOnEmpty, + } + ); + + setSessionLoading(finalSessionId, true); + activeJobs.current.set(generationId, newAbortController); + + // Wrapper to handle function calls (ReAct-style loop) + // IMPORTANT: We now receive the complete Part object which includes thoughtSignature + const handleFunctionCallResponse = async ( + usageMetadata: any, + groundingMetadata: any, + urlContextMetadata: any, + functionCallPart?: any // Part object containing functionCall and thoughtSignature + ) => { + const normalizedFunctionCallPart = (() => { + if (!functionCallPart) return functionCallPart; + const anyPart = functionCallPart as any; + const thoughtSignature = + anyPart.thoughtSignature || + anyPart.thought_signature || + anyPart.functionCall?.thoughtSignature || + anyPart.functionCall?.thought_signature; + + if (!thoughtSignature) return functionCallPart; + + return { + ...functionCallPart, + thoughtSignature, + thought_signature: thoughtSignature, + } as any; + })(); + + const functionCall = normalizedFunctionCallPart?.functionCall; + if (functionCall && functionCall.name === 'read_file' && projectContext) { + logService.info(`Executing function call: ${functionCall.name}`, { + args: functionCall.args, + hasThoughtSignature: !!(normalizedFunctionCallPart?.thoughtSignature || normalizedFunctionCallPart?.thought_signature) + }); + + try { + const filepath = functionCall.args?.filepath; + if (!filepath) { + throw new Error('Missing filepath argument'); + } + + // Read the file content + const fileContent = await readProjectFile(projectContext, filepath); + logService.info(`File read successfully: ${filepath}`, { length: fileContent.length }); + + // Build new history with function call and result + // CRITICAL: Use the COMPLETE Part object (with thoughtSignature) for model's turn + const updatedHistory = [ + ...historyForChat, + { role: 'user' as const, parts: finalParts }, + { + role: 'model' as const, + parts: [normalizedFunctionCallPart] // Use complete Part with thoughtSignature + }, + { + role: 'user' as const, + parts: [{ + functionResponse: { + name: functionCall.name, + response: { content: fileContent } + } + }] + } + ]; + + // Continue the conversation with function result + await geminiServiceInstance.sendMessageStream( + keyToUse, + activeModelId, + updatedHistory, + [], // Empty parts since context is in history + config, // Keep original config (thinking enabled) + newAbortController.signal, + streamOnPart, + onThoughtChunk, + streamOnError, + handleFunctionCallResponse, // Recursive for multi-turn + 'model' // Continue from model + ); + } catch (error) { + logService.error('Function call execution failed:', error); + // Continue with error message in response + const errorPart = { text: `\n\n[Error reading file: ${error instanceof Error ? error.message : 'Unknown error'}]` }; + streamOnPart(errorPart); + streamOnComplete(usageMetadata, groundingMetadata, urlContextMetadata); + } + } else { + // No function call, complete normally + streamOnComplete(usageMetadata, groundingMetadata, urlContextMetadata); + } + }; + + if (appSettings.isStreamingEnabled) { + await geminiServiceInstance.sendMessageStream( + keyToUse, + activeModelId, + historyForChat, + finalParts, + config, + newAbortController.signal, + streamOnPart, + onThoughtChunk, + streamOnError, + handleFunctionCallResponse, + finalRole + ); + } else { + await geminiServiceInstance.sendMessageNonStream( + keyToUse, + activeModelId, + historyForChat, + finalParts, + config, + newAbortController.signal, + streamOnError, + (parts, thoughts, usage, grounding) => { + for (const part of parts) streamOnPart(part); + if (thoughts) onThoughtChunk(thoughts); + streamOnComplete(usage, grounding); + } + ); + } + }, [appSettings, messages, getStreamHandlers, handleGenerateCanvas, setSessionLoading, activeJobs, projectContext]); + + return { performApiCall }; +}; diff --git a/all-model-chat/hooks/message-sender/standard/useSessionUpdate.ts b/all-model-chat/apps/web/hooks/message-sender/standard/useSessionUpdate.ts similarity index 100% rename from all-model-chat/hooks/message-sender/standard/useSessionUpdate.ts rename to all-model-chat/apps/web/hooks/message-sender/standard/useSessionUpdate.ts diff --git a/all-model-chat/hooks/message-sender/types.ts b/all-model-chat/apps/web/hooks/message-sender/types.ts similarity index 85% rename from all-model-chat/hooks/message-sender/types.ts rename to all-model-chat/apps/web/hooks/message-sender/types.ts index f5682f11..1fdfdeb6 100644 --- a/all-model-chat/hooks/message-sender/types.ts +++ b/all-model-chat/apps/web/hooks/message-sender/types.ts @@ -1,6 +1,6 @@ import React, { Dispatch, SetStateAction } from 'react'; -import { AppSettings, ChatMessage, UploadedFile, ChatSettings as IndividualChatSettings, SavedChatSession } from '../../types'; +import { AppSettings, ChatMessage, UploadedFile, ChatSettings as IndividualChatSettings, SavedChatSession, ProjectContext } from '../../types'; import { UsageMetadata } from '@google/genai'; export type SessionsUpdater = (updater: (prev: SavedChatSession[]) => SavedChatSession[]) => void; @@ -12,13 +12,19 @@ export interface StreamHandlerFunctions { onThoughtChunk: (thoughtChunk: string) => void; } +export interface StreamHandlerOptions { + onSuccess?: (generationId: string, finalContent: string) => void; + onEmptyResponse?: (generationId: string) => void; + suppressEmptyResponseError?: boolean; +} + export type GetStreamHandlers = ( currentSessionId: string, generationId: string, abortController: AbortController, generationStartTime: Date, currentChatSettings: IndividualChatSettings, - onSuccess?: (generationId: string, finalContent: string) => void + options?: StreamHandlerOptions ) => StreamHandlerFunctions; export interface BaseSenderProps { @@ -52,4 +58,6 @@ export interface StandardChatProps extends BaseSenderProps { getStreamHandlers: GetStreamHandlers; sessionKeyMapRef: React.MutableRefObject>; handleGenerateCanvas: (sourceMessageId: string, content: string) => Promise; + /** Active project context for agentic folder access */ + projectContext?: ProjectContext | null; } diff --git a/all-model-chat/hooks/message-sender/useApiErrorHandler.ts b/all-model-chat/apps/web/hooks/message-sender/useApiErrorHandler.ts similarity index 100% rename from all-model-chat/hooks/message-sender/useApiErrorHandler.ts rename to all-model-chat/apps/web/hooks/message-sender/useApiErrorHandler.ts diff --git a/all-model-chat/hooks/message-sender/useCanvasGenerator.ts b/all-model-chat/apps/web/hooks/message-sender/useCanvasGenerator.ts similarity index 100% rename from all-model-chat/hooks/message-sender/useCanvasGenerator.ts rename to all-model-chat/apps/web/hooks/message-sender/useCanvasGenerator.ts diff --git a/all-model-chat/hooks/message-sender/useChatStreamHandler.ts b/all-model-chat/apps/web/hooks/message-sender/useChatStreamHandler.ts similarity index 92% rename from all-model-chat/hooks/message-sender/useChatStreamHandler.ts rename to all-model-chat/apps/web/hooks/message-sender/useChatStreamHandler.ts index 9d53d023..aafacce5 100644 --- a/all-model-chat/hooks/message-sender/useChatStreamHandler.ts +++ b/all-model-chat/apps/web/hooks/message-sender/useChatStreamHandler.ts @@ -31,12 +31,15 @@ export const useChatStreamHandler = ({ abortController: AbortController, generationStartTime: Date, currentChatSettings: IndividualChatSettings, - onSuccess?: (generationId: string, finalContent: string) => void + options?: { onSuccess?: (generationId: string, finalContent: string) => void; onEmptyResponse?: (generationId: string) => void; suppressEmptyResponseError?: boolean } ) => { const newModelMessageIds = new Set([generationId]); let firstContentPartTime: Date | null = null; let accumulatedText = ""; let accumulatedThoughts = ""; + const onSuccess = options?.onSuccess; + const onEmptyResponse = options?.onEmptyResponse; + const suppressEmptyResponseError = options?.suppressEmptyResponseError ?? false; // Reset store for this new generation streamingStore.clear(generationId); @@ -66,6 +69,14 @@ export const useChatStreamHandler = ({ ); } + const isEmptyResponse = !accumulatedText.trim() && !accumulatedThoughts.trim(); + const shouldAutoContinue = !!onEmptyResponse && isEmptyResponse && !abortController.signal.aborted; + if (shouldAutoContinue) { + onEmptyResponse?.(generationId); + } + + const shouldSuppressEmptyError = suppressEmptyResponseError || shouldAutoContinue; + // Perform the Final Update to State (and DB) updateAndPersistSessions(prev => { const sessionIndex = prev.findIndex(s => s.id === currentSessionId); @@ -103,7 +114,7 @@ export const useChatStreamHandler = ({ usageMetadata, groundingMetadata, urlContextMetadata, - abortController.signal.aborted + abortController.signal.aborted || shouldSuppressEmptyError ); sessionToUpdate.messages = finalizationResult.updatedMessages; diff --git a/all-model-chat/hooks/message-sender/useImageEditSender.ts b/all-model-chat/apps/web/hooks/message-sender/useImageEditSender.ts similarity index 100% rename from all-model-chat/hooks/message-sender/useImageEditSender.ts rename to all-model-chat/apps/web/hooks/message-sender/useImageEditSender.ts diff --git a/all-model-chat/hooks/message-sender/useStandardChat.ts b/all-model-chat/apps/web/hooks/message-sender/useStandardChat.ts similarity index 68% rename from all-model-chat/hooks/message-sender/useStandardChat.ts rename to all-model-chat/apps/web/hooks/message-sender/useStandardChat.ts index 273f08d6..66e35875 100644 --- a/all-model-chat/hooks/message-sender/useStandardChat.ts +++ b/all-model-chat/apps/web/hooks/message-sender/useStandardChat.ts @@ -1,5 +1,5 @@ -import { useCallback } from 'react'; +import { useCallback, useEffect, useRef } from 'react'; import { generateUniqueId, buildContentParts, getKeyForRequest, performOptimisticSessionUpdate, logService } from '../../utils/appUtils'; import { DEFAULT_CHAT_SETTINGS, MODELS_SUPPORTING_RAW_MODE } from '../../constants/appConstants'; import { UploadedFile, ChatMessage } from '../../types'; @@ -27,7 +27,17 @@ export const useStandardChat = ({ getStreamHandlers, sessionKeyMapRef, handleGenerateCanvas, + projectContext, }: StandardChatProps) => { + const autoContinueAttemptsRef = useRef>(new Set()); + const sendStandardMessageRef = useRef<(( + textToUse: string, + filesToUse: UploadedFile[], + effectiveEditingId: string | null, + activeModelId: string, + isContinueMode?: boolean, + isFastMode?: boolean + ) => Promise) | null>(null); const { updateSessionState } = useSessionUpdate({ appSettings, @@ -37,60 +47,75 @@ export const useStandardChat = ({ sessionKeyMapRef }); + const handleAutoContinue = useCallback((params: { generationId: string; activeModelId: string; effectiveEditingId: string | null }) => { + const { generationId, activeModelId, effectiveEditingId } = params; + if (autoContinueAttemptsRef.current.has(generationId)) return; + autoContinueAttemptsRef.current.add(generationId); + + const sender = sendStandardMessageRef.current; + if (!sender) return; + + setTimeout(() => { + sender('', [], effectiveEditingId || generationId, activeModelId, true, false); + }, 0); + }, []); + const { performApiCall } = useApiInteraction({ appSettings, messages, getStreamHandlers, handleGenerateCanvas, setSessionLoading, - activeJobs + activeJobs, + projectContext, + onAutoContinue: handleAutoContinue, }); const sendStandardMessage = useCallback(async ( - textToUse: string, - filesToUse: UploadedFile[], - effectiveEditingId: string | null, + textToUse: string, + filesToUse: UploadedFile[], + effectiveEditingId: string | null, activeModelId: string, isContinueMode: boolean = false, isFastMode: boolean = false ) => { const settingsForPersistence = { ...currentChatSettings }; let settingsForApi = { ...currentChatSettings }; - + if (isFastMode) { const isGemini3Flash = activeModelId.includes('gemini-3') && activeModelId.includes('flash'); const targetLevel = isGemini3Flash ? 'MINIMAL' : 'LOW'; settingsForApi.thinkingLevel = targetLevel; - settingsForApi.thinkingBudget = 0; + settingsForApi.thinkingBudget = 0; logService.info(`Fast Mode activated (One-off): Overriding thinking level to ${targetLevel}.`); } const keyResult = getKeyForRequest(appSettings, settingsForApi); if ('error' in keyResult) { logService.error("Send message failed: API Key not configured."); - const errorMsg: ChatMessage = { id: generateUniqueId(), role: 'error', content: keyResult.error, timestamp: new Date() }; - const newSessionId = generateUniqueId(); - - updateAndPersistSessions(prev => performOptimisticSessionUpdate(prev, { - activeSessionId: null, - newSessionId, - newMessages: [errorMsg], - settings: { ...DEFAULT_CHAT_SETTINGS, ...appSettings }, - appSettings, - title: "API Key Error" - })); - setActiveSessionId(newSessionId); + const errorMsg: ChatMessage = { id: generateUniqueId(), role: 'error', content: keyResult.error, timestamp: new Date() }; + const newSessionId = generateUniqueId(); + + updateAndPersistSessions(prev => performOptimisticSessionUpdate(prev, { + activeSessionId: null, + newSessionId, + newMessages: [errorMsg], + settings: { ...DEFAULT_CHAT_SETTINGS, ...appSettings }, + appSettings, + title: "API Key Error" + })); + setActiveSessionId(newSessionId); return; } const { key: keyToUse, isNewKey } = keyResult; const shouldLockKey = isNewKey && filesToUse.some(f => f.fileUri && f.uploadState === 'active'); const newAbortController = new AbortController(); - + let generationId: string; let generationStartTime: Date; - + if (isContinueMode && effectiveEditingId) { generationId = effectiveEditingId; const targetMsg = messages.find(m => m.id === effectiveEditingId); @@ -99,22 +124,22 @@ export const useStandardChat = ({ generationId = generateUniqueId(); generationStartTime = new Date(); } - + const successfullyProcessedFiles = filesToUse.filter(f => f.uploadState === 'active' && !f.error && !f.isProcessing); - + const { contentParts: promptParts, enrichedFiles } = await buildContentParts( - textToUse.trim(), + textToUse.trim(), successfullyProcessedFiles, activeModelId, settingsForApi.mediaResolution ); - + const finalSessionId = activeSessionId || generateUniqueId(); - - const isRawMode = (settingsForApi.isRawModeEnabled ?? appSettings.isRawModeEnabled) - && !isContinueMode + + const isRawMode = (settingsForApi.isRawModeEnabled ?? appSettings.isRawModeEnabled) + && !isContinueMode && MODELS_SUPPORTING_RAW_MODE.some(m => activeModelId.includes(m)); - + updateSessionState({ activeSessionId, finalSessionId, @@ -131,7 +156,7 @@ export const useStandardChat = ({ }); userScrolledUp.current = false; - + await performApiCall({ finalSessionId, generationId, @@ -151,9 +176,13 @@ export const useStandardChat = ({ }); }, [ - appSettings, currentChatSettings, messages, aspectRatio, imageSize, activeSessionId, + appSettings, currentChatSettings, messages, aspectRatio, imageSize, activeSessionId, updateAndPersistSessions, setActiveSessionId, userScrolledUp, updateSessionState, performApiCall ]); + useEffect(() => { + sendStandardMessageRef.current = sendStandardMessage; + }, [sendStandardMessage]); + return { sendStandardMessage }; }; diff --git a/all-model-chat/hooks/message-sender/useTtsImagenSender.ts b/all-model-chat/apps/web/hooks/message-sender/useTtsImagenSender.ts similarity index 100% rename from all-model-chat/hooks/message-sender/useTtsImagenSender.ts rename to all-model-chat/apps/web/hooks/message-sender/useTtsImagenSender.ts diff --git a/all-model-chat/hooks/text-selection/useSelectionAudio.ts b/all-model-chat/apps/web/hooks/text-selection/useSelectionAudio.ts similarity index 100% rename from all-model-chat/hooks/text-selection/useSelectionAudio.ts rename to all-model-chat/apps/web/hooks/text-selection/useSelectionAudio.ts diff --git a/all-model-chat/hooks/text-selection/useSelectionDrag.ts b/all-model-chat/apps/web/hooks/text-selection/useSelectionDrag.ts similarity index 100% rename from all-model-chat/hooks/text-selection/useSelectionDrag.ts rename to all-model-chat/apps/web/hooks/text-selection/useSelectionDrag.ts diff --git a/all-model-chat/hooks/text-selection/useSelectionPosition.ts b/all-model-chat/apps/web/hooks/text-selection/useSelectionPosition.ts similarity index 100% rename from all-model-chat/hooks/text-selection/useSelectionPosition.ts rename to all-model-chat/apps/web/hooks/text-selection/useSelectionPosition.ts diff --git a/all-model-chat/hooks/ui/useCodeBlock.ts b/all-model-chat/apps/web/hooks/ui/useCodeBlock.ts similarity index 100% rename from all-model-chat/hooks/ui/useCodeBlock.ts rename to all-model-chat/apps/web/hooks/ui/useCodeBlock.ts diff --git a/all-model-chat/hooks/ui/useHtmlPreviewModal.ts b/all-model-chat/apps/web/hooks/ui/useHtmlPreviewModal.ts similarity index 100% rename from all-model-chat/hooks/ui/useHtmlPreviewModal.ts rename to all-model-chat/apps/web/hooks/ui/useHtmlPreviewModal.ts diff --git a/all-model-chat/hooks/ui/useImageNavigation.ts b/all-model-chat/apps/web/hooks/ui/useImageNavigation.ts similarity index 100% rename from all-model-chat/hooks/ui/useImageNavigation.ts rename to all-model-chat/apps/web/hooks/ui/useImageNavigation.ts diff --git a/all-model-chat/hooks/ui/useMessageStream.ts b/all-model-chat/apps/web/hooks/ui/useMessageStream.ts similarity index 100% rename from all-model-chat/hooks/ui/useMessageStream.ts rename to all-model-chat/apps/web/hooks/ui/useMessageStream.ts diff --git a/all-model-chat/hooks/ui/usePdfViewer.ts b/all-model-chat/apps/web/hooks/ui/usePdfViewer.ts similarity index 97% rename from all-model-chat/hooks/ui/usePdfViewer.ts rename to all-model-chat/apps/web/hooks/ui/usePdfViewer.ts index 31059c74..34e65e2f 100644 --- a/all-model-chat/hooks/ui/usePdfViewer.ts +++ b/all-model-chat/apps/web/hooks/ui/usePdfViewer.ts @@ -4,7 +4,10 @@ import { pdfjs } from 'react-pdf'; import { UploadedFile } from '../../types'; // Configure PDF worker globally -pdfjs.GlobalWorkerOptions.workerSrc = `https://esm.sh/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs`; +pdfjs.GlobalWorkerOptions.workerSrc = new URL( + 'pdfjs-dist/build/pdf.worker.min.mjs', + import.meta.url +).toString(); // Determine responsive initial scale const getInitialScale = () => { diff --git a/all-model-chat/hooks/ui/useSmoothStreaming.ts b/all-model-chat/apps/web/hooks/ui/useSmoothStreaming.ts similarity index 100% rename from all-model-chat/hooks/ui/useSmoothStreaming.ts rename to all-model-chat/apps/web/hooks/ui/useSmoothStreaming.ts diff --git a/all-model-chat/hooks/useAudioRecorder.ts b/all-model-chat/apps/web/hooks/useAudioRecorder.ts similarity index 100% rename from all-model-chat/hooks/useAudioRecorder.ts rename to all-model-chat/apps/web/hooks/useAudioRecorder.ts diff --git a/all-model-chat/hooks/useClickOutside.ts b/all-model-chat/apps/web/hooks/useClickOutside.ts similarity index 100% rename from all-model-chat/hooks/useClickOutside.ts rename to all-model-chat/apps/web/hooks/useClickOutside.ts diff --git a/all-model-chat/hooks/useCodeBlock.ts b/all-model-chat/apps/web/hooks/useCodeBlock.ts similarity index 100% rename from all-model-chat/hooks/useCodeBlock.ts rename to all-model-chat/apps/web/hooks/useCodeBlock.ts diff --git a/all-model-chat/hooks/useCopyToClipboard.ts b/all-model-chat/apps/web/hooks/useCopyToClipboard.ts similarity index 100% rename from all-model-chat/hooks/useCopyToClipboard.ts rename to all-model-chat/apps/web/hooks/useCopyToClipboard.ts diff --git a/all-model-chat/hooks/useCreateFileEditor.ts b/all-model-chat/apps/web/hooks/useCreateFileEditor.ts similarity index 100% rename from all-model-chat/hooks/useCreateFileEditor.ts rename to all-model-chat/apps/web/hooks/useCreateFileEditor.ts diff --git a/all-model-chat/hooks/useDataManagement.ts b/all-model-chat/apps/web/hooks/useDataManagement.ts similarity index 100% rename from all-model-chat/hooks/useDataManagement.ts rename to all-model-chat/apps/web/hooks/useDataManagement.ts diff --git a/all-model-chat/hooks/useDevice.ts b/all-model-chat/apps/web/hooks/useDevice.ts similarity index 100% rename from all-model-chat/hooks/useDevice.ts rename to all-model-chat/apps/web/hooks/useDevice.ts diff --git a/all-model-chat/apps/web/hooks/useFolderToolExecutor.ts b/all-model-chat/apps/web/hooks/useFolderToolExecutor.ts new file mode 100644 index 00000000..491c4594 --- /dev/null +++ b/all-model-chat/apps/web/hooks/useFolderToolExecutor.ts @@ -0,0 +1,108 @@ + +import { useCallback, useState } from 'react'; +import { ProjectContext, ProjectContextReadState } from '../types'; +import { readProjectFile } from '../utils/folderImportUtils'; + +interface UseFolderToolExecutorProps { + projectContext: ProjectContext | null; +} + +interface UseFolderToolExecutorReturn { + readState: ProjectContextReadState; + /** Execute a read_file function call from the AI */ + executeReadFile: (filepath: string) => Promise; + /** Reset the read state (e.g., when starting a new conversation) */ + resetReadState: () => void; + /** Client functions registry for tool handling */ + clientFunctions: Record Promise>; +} + +/** + * Hook for executing client-side folder access tools. + * This is analogous to useLiveTools but for standard chat with folder context. + * + * When AI calls read_file, this hook: + * 1. Updates the loading state (for UI feedback) + * 2. Reads the file from the stored ProjectContext + * 3. Tracks which files have been read + * 4. Returns the file content to be sent back as function response + */ +export const useFolderToolExecutor = ({ projectContext }: UseFolderToolExecutorProps): UseFolderToolExecutorReturn => { + const [readState, setReadState] = useState({ + readFiles: new Set(), + loadingFile: null, + }); + + const executeReadFile = useCallback(async (filepath: string): Promise => { + if (!projectContext) { + throw new Error('No project context available'); + } + + // Set loading state + setReadState(prev => ({ + ...prev, + loadingFile: filepath, + })); + + try { + const content = await readProjectFile(projectContext, filepath); + + // Update read state + setReadState(prev => ({ + readFiles: new Set([...prev.readFiles, filepath]), + loadingFile: null, + })); + + return content; + } catch (error) { + // Clear loading state on error + setReadState(prev => ({ + ...prev, + loadingFile: null, + })); + throw error; + } + }, [projectContext]); + + const resetReadState = useCallback(() => { + setReadState({ + readFiles: new Set(), + loadingFile: null, + }); + }, []); + + // Client functions registry for use with tool handling + const clientFunctions = { + read_file: async (args: { filepath: string }) => { + return executeReadFile(args.filepath); + } + }; + + return { + readState, + executeReadFile, + resetReadState, + clientFunctions, + }; +}; + +/** + * Generate the system prompt prefix for agentic folder access. + * This should be prepended to the user's system instruction. + */ +export const generateProjectContextSystemPrompt = (projectContext: ProjectContext): string => { + return `You have access to a project with the following file structure: + +\`\`\` +${projectContext.fileTree} +\`\`\` + +**Important instructions for working with this project:** +1. When the user asks about code, files, or the project structure, use the \`read_file\` tool to read the relevant files. +2. Do NOT guess or assume file contents - always read the file first. +3. Use the filepath exactly as shown in the structure above (e.g., "src/App.tsx", not "./src/App.tsx"). +4. You can read multiple files if needed to answer the question. +5. After reading a file, analyze it thoroughly before responding. + +`; +}; diff --git a/all-model-chat/hooks/useHistorySidebarLogic.ts b/all-model-chat/apps/web/hooks/useHistorySidebarLogic.ts similarity index 100% rename from all-model-chat/hooks/useHistorySidebarLogic.ts rename to all-model-chat/apps/web/hooks/useHistorySidebarLogic.ts diff --git a/all-model-chat/hooks/useHtmlPreviewModal.ts b/all-model-chat/apps/web/hooks/useHtmlPreviewModal.ts similarity index 100% rename from all-model-chat/hooks/useHtmlPreviewModal.ts rename to all-model-chat/apps/web/hooks/useHtmlPreviewModal.ts diff --git a/all-model-chat/hooks/useLiveAPI.ts b/all-model-chat/apps/web/hooks/useLiveAPI.ts similarity index 100% rename from all-model-chat/hooks/useLiveAPI.ts rename to all-model-chat/apps/web/hooks/useLiveAPI.ts diff --git a/all-model-chat/hooks/useMessageExport.ts b/all-model-chat/apps/web/hooks/useMessageExport.ts similarity index 100% rename from all-model-chat/hooks/useMessageExport.ts rename to all-model-chat/apps/web/hooks/useMessageExport.ts diff --git a/all-model-chat/hooks/useMessageHandler.ts b/all-model-chat/apps/web/hooks/useMessageHandler.ts similarity index 90% rename from all-model-chat/hooks/useMessageHandler.ts rename to all-model-chat/apps/web/hooks/useMessageHandler.ts index 4853bd5a..4a53a419 100644 --- a/all-model-chat/hooks/useMessageHandler.ts +++ b/all-model-chat/apps/web/hooks/useMessageHandler.ts @@ -1,6 +1,6 @@ import React, { Dispatch, SetStateAction } from 'react'; -import { AppSettings, ChatMessage, UploadedFile, ChatSettings as IndividualChatSettings, SavedChatSession, InputCommand } from '../types'; +import { AppSettings, ChatMessage, UploadedFile, ChatSettings as IndividualChatSettings, SavedChatSession, InputCommand, ProjectContext } from '../types'; import { useMessageSender } from './useMessageSender'; import { useMessageActions } from './chat/messages/useMessageActions'; import { useTextToSpeechHandler } from './chat/messages/useTextToSpeechHandler'; @@ -33,15 +33,17 @@ interface MessageHandlerProps { scrollContainerRef: React.RefObject; sessionKeyMapRef: React.MutableRefObject>; language: 'en' | 'zh'; - setSessionLoading: (sessionId: string, isLoading: boolean) => void; + setSessionLoading: (sessionId: string, isLoading: boolean) => void; + /** Active project context for agentic folder access */ + projectContext?: ProjectContext | null; } export const useMessageHandler = (props: MessageHandlerProps) => { - const { - messages, - isLoading, - activeSessionId, - editingMessageId, + const { + messages, + isLoading, + activeSessionId, + editingMessageId, activeJobs, setCommandedInput, setSelectedFiles, @@ -53,9 +55,9 @@ export const useMessageHandler = (props: MessageHandlerProps) => { setLoadingSessionIds, setSessionLoading } = props; - + const { handleSendMessage, handleGenerateCanvas } = useMessageSender(props); - + const messageActions = useMessageActions({ messages, isLoading, @@ -73,7 +75,7 @@ export const useMessageHandler = (props: MessageHandlerProps) => { setLoadingSessionIds, setSessionLoading }); - + const { handleTextToSpeech, handleQuickTTS } = useTextToSpeechHandler(props); return { diff --git a/all-model-chat/hooks/useMessageListUI.ts b/all-model-chat/apps/web/hooks/useMessageListUI.ts similarity index 100% rename from all-model-chat/hooks/useMessageListUI.ts rename to all-model-chat/apps/web/hooks/useMessageListUI.ts diff --git a/all-model-chat/hooks/useMessageSender.ts b/all-model-chat/apps/web/hooks/useMessageSender.ts similarity index 89% rename from all-model-chat/hooks/useMessageSender.ts rename to all-model-chat/apps/web/hooks/useMessageSender.ts index d235a846..eb3e1857 100644 --- a/all-model-chat/hooks/useMessageSender.ts +++ b/all-model-chat/apps/web/hooks/useMessageSender.ts @@ -1,6 +1,6 @@ import React, { useCallback, Dispatch, SetStateAction } from 'react'; -import { AppSettings, ChatMessage, UploadedFile, ChatSettings as IndividualChatSettings, SavedChatSession } from '../types'; +import { AppSettings, ChatMessage, UploadedFile, ChatSettings as IndividualChatSettings, SavedChatSession, ProjectContext } from '../types'; import { generateUniqueId, getKeyForRequest, generateSessionTitle, logService, createNewSession } from '../utils/appUtils'; import { DEFAULT_CHAT_SETTINGS } from '../constants/appConstants'; import { useChatStreamHandler } from './message-sender/useChatStreamHandler'; @@ -32,6 +32,8 @@ interface MessageSenderProps { sessionKeyMapRef: React.MutableRefObject>; language: 'en' | 'zh'; setSessionLoading: (sessionId: string, isLoading: boolean) => void; + /** Active project context for agentic folder access */ + projectContext?: ProjectContext | null; } export const useMessageSender = (props: MessageSenderProps) => { @@ -63,9 +65,9 @@ export const useMessageSender = (props: MessageSenderProps) => { }); // Initialize Sub-Hooks - const { handleGenerateCanvas } = useCanvasGenerator({ - ...props, - getStreamHandlers + const { handleGenerateCanvas } = useCanvasGenerator({ + ...props, + getStreamHandlers }); const { sendStandardMessage } = useStandardChat({ @@ -74,13 +76,13 @@ export const useMessageSender = (props: MessageSenderProps) => { handleGenerateCanvas }); - const { handleTtsImagenMessage } = useTtsImagenSender({ + const { handleTtsImagenMessage } = useTtsImagenSender({ updateAndPersistSessions, setSessionLoading, activeJobs, setActiveSessionId }); - + const { handleImageEditMessage } = useImageEditSender({ updateAndPersistSessions, setSessionLoading, @@ -94,7 +96,7 @@ export const useMessageSender = (props: MessageSenderProps) => { const effectiveEditingId = overrideOptions?.editingId ?? editingMessageId; const isContinueMode = overrideOptions?.isContinueMode ?? false; const isFastMode = overrideOptions?.isFastMode ?? false; - + const sessionToUpdate = currentChatSettings; const activeModelId = sessionToUpdate.modelId; const isTtsModel = activeModelId.includes('-tts'); @@ -106,30 +108,30 @@ export const useMessageSender = (props: MessageSenderProps) => { if (!textToUse.trim() && !isTtsModel && !isImagenModel && !isContinueMode && filesToUse.filter(f => f.uploadState === 'active').length === 0) return; if ((isTtsModel || isImagenModel || isImageEditModel || isGemini3Image) && !textToUse.trim()) return; - if (filesToUse.some(f => f.isProcessing || (f.uploadState !== 'active' && !f.error) )) { + if (filesToUse.some(f => f.isProcessing || (f.uploadState !== 'active' && !f.error))) { logService.warn("Send message blocked: files are still processing."); - setAppFileError("Wait for files to finish processing."); - return; + setAppFileError("Wait for files to finish processing."); + return; } - + setAppFileError(null); - if (!activeModelId) { + if (!activeModelId) { logService.error("Send message failed: No model selected."); const errorMsg: ChatMessage = { id: generateUniqueId(), role: 'error', content: 'No model selected.', timestamp: new Date() }; const newSession = createNewSession({ ...DEFAULT_CHAT_SETTINGS, ...appSettings }, [errorMsg], "Error"); updateAndPersistSessions(p => [newSession, ...p]); setActiveSessionId(newSession.id); - return; + return; } const keyResult = getKeyForRequest(appSettings, sessionToUpdate); if ('error' in keyResult) { logService.error("Send message failed: API Key not configured."); - const errorMsg: ChatMessage = { id: generateUniqueId(), role: 'error', content: keyResult.error, timestamp: new Date() }; - const newSession = createNewSession({ ...DEFAULT_CHAT_SETTINGS, ...appSettings }, [errorMsg], "API Key Error"); - updateAndPersistSessions(p => [newSession, ...p]); - setActiveSessionId(newSession.id); + const errorMsg: ChatMessage = { id: generateUniqueId(), role: 'error', content: keyResult.error, timestamp: new Date() }; + const newSession = createNewSession({ ...DEFAULT_CHAT_SETTINGS, ...appSettings }, [errorMsg], "API Key Error"); + updateAndPersistSessions(p => [newSession, ...p]); + setActiveSessionId(newSession.id); return; } const { key: keyToUse, isNewKey } = keyResult; @@ -137,7 +139,7 @@ export const useMessageSender = (props: MessageSenderProps) => { const newAbortController = new AbortController(); const generationId = generateUniqueId(); - + if (appSettings.isAutoScrollOnSendEnabled) { userScrolledUp.current = false; } @@ -148,7 +150,7 @@ export const useMessageSender = (props: MessageSenderProps) => { if (editingMessageId) setEditingMessageId(null); return; } - + if (isImageEditModel || (isGemini3Image && appSettings.generateQuadImages)) { const editIndex = effectiveEditingId ? messages.findIndex(m => m.id === effectiveEditingId) : -1; const historyMessages = editIndex !== -1 ? messages.slice(0, editIndex) : messages; @@ -156,7 +158,7 @@ export const useMessageSender = (props: MessageSenderProps) => { if (editingMessageId) setEditingMessageId(null); return; } - + await sendStandardMessage(textToUse, filesToUse, effectiveEditingId, activeModelId, isContinueMode, isFastMode); }, [ diff --git a/all-model-chat/hooks/useModelCapabilities.ts b/all-model-chat/apps/web/hooks/useModelCapabilities.ts similarity index 100% rename from all-model-chat/hooks/useModelCapabilities.ts rename to all-model-chat/apps/web/hooks/useModelCapabilities.ts diff --git a/all-model-chat/hooks/usePdfViewer.ts b/all-model-chat/apps/web/hooks/usePdfViewer.ts similarity index 100% rename from all-model-chat/hooks/usePdfViewer.ts rename to all-model-chat/apps/web/hooks/usePdfViewer.ts diff --git a/all-model-chat/hooks/usePreloadedScenarios.ts b/all-model-chat/apps/web/hooks/usePreloadedScenarios.ts similarity index 94% rename from all-model-chat/hooks/usePreloadedScenarios.ts rename to all-model-chat/apps/web/hooks/usePreloadedScenarios.ts index 9fc58c9d..59363b4d 100644 --- a/all-model-chat/hooks/usePreloadedScenarios.ts +++ b/all-model-chat/apps/web/hooks/usePreloadedScenarios.ts @@ -114,9 +114,12 @@ export const usePreloadedScenarios = ({ appSettings, setAppSettings, updateAndPe const newSession = createNewSession(sessionSettings, messages, title); - updateAndPersistSessions(prev => [newSession, ...prev.filter(s => s.messages.length > 0)]); + // Keep existing sessions intact to avoid accidental history loss. + updateAndPersistSessions(prev => [newSession, ...prev]); setActiveSessionId(newSession.id); - dbService.setActiveSessionId(newSession.id); + dbService.setActiveSessionId(newSession.id).catch(error => { + logService.error('Failed to persist active session after loading scenario', { error }); + }); // Also update the global/default system prompt in app settings setAppSettings(prev => ({ diff --git a/all-model-chat/hooks/useSlashCommands.ts b/all-model-chat/apps/web/hooks/useSlashCommands.ts similarity index 100% rename from all-model-chat/hooks/useSlashCommands.ts rename to all-model-chat/apps/web/hooks/useSlashCommands.ts diff --git a/all-model-chat/hooks/useTextAreaInsert.ts b/all-model-chat/apps/web/hooks/useTextAreaInsert.ts similarity index 100% rename from all-model-chat/hooks/useTextAreaInsert.ts rename to all-model-chat/apps/web/hooks/useTextAreaInsert.ts diff --git a/all-model-chat/hooks/useVoiceInput.ts b/all-model-chat/apps/web/hooks/useVoiceInput.ts similarity index 100% rename from all-model-chat/hooks/useVoiceInput.ts rename to all-model-chat/apps/web/hooks/useVoiceInput.ts diff --git a/all-model-chat/index.html b/all-model-chat/apps/web/index.html similarity index 65% rename from all-model-chat/index.html rename to all-model-chat/apps/web/index.html index 52ab3aec..7bff39c9 100644 --- a/all-model-chat/index.html +++ b/all-model-chat/apps/web/index.html @@ -23,9 +23,6 @@ integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous" /> - - - @@ -33,50 +30,9 @@ - -
- \ No newline at end of file + diff --git a/all-model-chat/index.tsx b/all-model-chat/apps/web/index.tsx similarity index 83% rename from all-model-chat/index.tsx rename to all-model-chat/apps/web/index.tsx index bf7a4874..f48f3013 100644 --- a/all-model-chat/index.tsx +++ b/all-model-chat/apps/web/index.tsx @@ -7,6 +7,8 @@ import App from './App'; import './styles/main.css'; import './styles/animations.css'; import './styles/markdown.css'; +import 'react-pdf/dist/Page/AnnotationLayer.css'; +import 'react-pdf/dist/Page/TextLayer.css'; const rootElement = document.getElementById('root'); if (!rootElement) { diff --git a/all-model-chat/manifest.json b/all-model-chat/apps/web/manifest.json similarity index 100% rename from all-model-chat/manifest.json rename to all-model-chat/apps/web/manifest.json diff --git a/all-model-chat/metadata.json b/all-model-chat/apps/web/metadata.json similarity index 100% rename from all-model-chat/metadata.json rename to all-model-chat/apps/web/metadata.json diff --git a/all-model-chat/apps/web/package.json b/all-model-chat/apps/web/package.json new file mode 100644 index 00000000..9912e70b --- /dev/null +++ b/all-model-chat/apps/web/package.json @@ -0,0 +1,12 @@ +{ + "name": "@all-model-chat/web", + "private": true, + "version": "1.8.5", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "test": "node --test" + } +} diff --git a/all-model-chat/services/api/baseApi.ts b/all-model-chat/apps/web/services/api/baseApi.ts similarity index 66% rename from all-model-chat/services/api/baseApi.ts rename to all-model-chat/apps/web/services/api/baseApi.ts index 18203f64..64a8b7c2 100644 --- a/all-model-chat/services/api/baseApi.ts +++ b/all-model-chat/apps/web/services/api/baseApi.ts @@ -13,39 +13,39 @@ const MAX_POLLING_DURATION_MS = 10 * 60 * 1000; // 10 minutes export { POLLING_INTERVAL_MS, MAX_POLLING_DURATION_MS }; export const getClient = (apiKey: string, baseUrl?: string | null, httpOptions?: any): GoogleGenAI => { - try { - // Sanitize the API key to replace common non-ASCII characters that might - // be introduced by copy-pasting from rich text editors. This prevents - // "Failed to execute 'append' on 'Headers': Invalid character" errors. - const sanitizedApiKey = apiKey - .replace(/[\u2013\u2014]/g, '-') // en-dash, em-dash to hyphen - .replace(/[\u2018\u2019]/g, "'") // smart single quotes to apostrophe - .replace(/[\u201C\u201D]/g, '"') // smart double quotes to quote - .replace(/[\u00A0]/g, ' '); // non-breaking space to regular space - - if (apiKey !== sanitizedApiKey) { - logService.warn("API key was sanitized. Non-ASCII characters were replaced."); - } - - const config: any = { apiKey: sanitizedApiKey }; - - // Use the SDK's native baseUrl support if provided. - // This is more robust than the network interceptor for SDK-generated requests. - if (baseUrl && baseUrl.trim().length > 0) { - // Remove trailing slash for consistency - config.baseUrl = baseUrl.trim().replace(/\/$/, ''); - } - - if (httpOptions) { - config.httpOptions = httpOptions; - } - - return new GoogleGenAI(config); - } catch (error) { - logService.error("Failed to initialize GoogleGenAI client:", error); - // Re-throw to be caught by the calling function - throw error; - } + try { + // Sanitize the API key to replace common non-ASCII characters that might + // be introduced by copy-pasting from rich text editors. This prevents + // "Failed to execute 'append' on 'Headers': Invalid character" errors. + const sanitizedApiKey = apiKey + .replace(/[\u2013\u2014]/g, '-') // en-dash, em-dash to hyphen + .replace(/[\u2018\u2019]/g, "'") // smart single quotes to apostrophe + .replace(/[\u201C\u201D]/g, '"') // smart double quotes to quote + .replace(/[\u00A0]/g, ' '); // non-breaking space to regular space + + if (apiKey !== sanitizedApiKey) { + logService.warn("API key was sanitized. Non-ASCII characters were replaced."); + } + + const config: any = { apiKey: sanitizedApiKey }; + + // Use the SDK's native baseUrl support if provided. + // This is more robust than the network interceptor for SDK-generated requests. + if (baseUrl && baseUrl.trim().length > 0) { + // Remove trailing slash for consistency + config.baseUrl = baseUrl.trim().replace(/\/$/, ''); + } + + if (httpOptions) { + config.httpOptions = httpOptions; + } + + return new GoogleGenAI(config); + } catch (error) { + logService.error("Failed to initialize GoogleGenAI client:", error); + // Re-throw to be caught by the calling function + throw error; + } }; export const getApiClient = (apiKey?: string | null, baseUrl?: string | null, httpOptions?: any): GoogleGenAI => { @@ -63,19 +63,19 @@ export const getApiClient = (apiKey?: string | null, baseUrl?: string | null, ht */ export const getConfiguredApiClient = async (apiKey: string, httpOptions?: any): Promise => { const settings = await dbService.getAppSettings(); - + // Only use the proxy URL if Custom Config AND Use Proxy are both enabled // Explicitly check for truthiness to handle undefined/null const shouldUseProxy = !!(settings?.useCustomApiConfig && settings?.useApiProxy); const apiProxyUrl = shouldUseProxy ? settings?.apiProxyUrl : null; - + if (settings?.useCustomApiConfig && !shouldUseProxy) { // Debugging aid: if user expects proxy but it's not active if (settings?.apiProxyUrl && !settings?.useApiProxy) { - logService.debug("[API Config] Proxy URL present but 'Use API Proxy' toggle is OFF."); + logService.debug("[API Config] Proxy URL present but 'Use API Proxy' toggle is OFF."); } } - + return getClient(apiKey, apiProxyUrl, httpOptions); }; @@ -93,12 +93,14 @@ export const buildGenerationConfig = ( isDeepSearchEnabled?: boolean, imageSize?: string, safetySettings?: SafetySetting[], - mediaResolution?: MediaResolution + mediaResolution?: MediaResolution, + /** ASCII tree of project files for agentic folder access */ + projectContextTree?: string, ): any => { if (modelId === 'gemini-2.5-flash-image-preview' || modelId === 'gemini-2.5-flash-image') { const imageConfig: any = {}; if (aspectRatio && aspectRatio !== 'Auto') imageConfig.aspectRatio = aspectRatio; - + const config: any = { responseModalities: [Modality.IMAGE, Modality.TEXT], }; @@ -109,31 +111,31 @@ export const buildGenerationConfig = ( } if (modelId === 'gemini-3-pro-image-preview') { - const imageConfig: any = { + const imageConfig: any = { imageSize: imageSize || '1K', - }; - if (aspectRatio && aspectRatio !== 'Auto') { + }; + if (aspectRatio && aspectRatio !== 'Auto') { imageConfig.aspectRatio = aspectRatio; - } - - const config: any = { + } + + const config: any = { responseModalities: ['IMAGE', 'TEXT'], imageConfig, - }; - - // Add tools if enabled - const tools = []; - if (isGoogleSearchEnabled || isDeepSearchEnabled) tools.push({ googleSearch: {} }); - if (tools.length > 0) config.tools = tools; - - if (systemInstruction) config.systemInstruction = systemInstruction; - - return config; + }; + + // Add tools if enabled + const tools = []; + if (isGoogleSearchEnabled || isDeepSearchEnabled) tools.push({ googleSearch: {} }); + if (tools.length > 0) config.tools = tools; + + if (systemInstruction) config.systemInstruction = systemInstruction; + + return config; } - + let finalSystemInstruction = systemInstruction; if (isDeepSearchEnabled) { - finalSystemInstruction = finalSystemInstruction + finalSystemInstruction = finalSystemInstruction ? `${finalSystemInstruction}\n\n${DEEP_SEARCH_SYSTEM_PROMPT}` : DEEP_SEARCH_SYSTEM_PROMPT; } @@ -150,11 +152,11 @@ export const buildGenerationConfig = ( const isGemini3 = isGemini3Model(modelId); // Gemma models do not support media resolution at all const isGemma = modelId.toLowerCase().includes('gemma'); - + if (!isGemini3 && !isGemma && mediaResolution) { // For non-Gemini 3 models (and not Gemma), apply global resolution if specified generationConfig.mediaResolution = mediaResolution; - } + } // Note: For Gemini 3, we don't set global mediaResolution here because we inject it into parts in `buildContentParts`. // The API documentation says per-part overrides global, but to be clean/explicit as requested ("become Per-part"), // we skip global for G3. @@ -204,12 +206,32 @@ export const buildGenerationConfig = ( tools.push({ urlContext: {} }); } + // Agentic folder access: add read_file function declaration + if (projectContextTree) { + tools.push({ + functionDeclarations: [{ + name: "read_file", + description: "Read the content of a file from the user's project. Use this when you need to see code or content to answer a question about the project. Only call this for files listed in the project structure.", + parameters: { + type: "OBJECT", + properties: { + filepath: { + type: "STRING", + description: "The relative path of the file to read, exactly as shown in the project structure (e.g., 'src/App.tsx' or 'package.json')" + } + }, + required: ["filepath"] + } + }] + }); + } + if (tools.length > 0) { generationConfig.tools = tools; // When using tools, these should not be set delete generationConfig.responseMimeType; delete generationConfig.responseSchema; } - + return generationConfig; }; diff --git a/all-model-chat/apps/web/services/api/bffApi.ts b/all-model-chat/apps/web/services/api/bffApi.ts new file mode 100644 index 00000000..0cda1b47 --- /dev/null +++ b/all-model-chat/apps/web/services/api/bffApi.ts @@ -0,0 +1,57 @@ +import type { BffErrorPayload } from '@all-model-chat/shared-api'; + +export const resolveBffEndpoint = (path: string): string => { + const env = (import.meta as ImportMeta & { env?: Record }).env; + const configuredBaseUrl = + typeof env?.VITE_BFF_BASE_URL === 'string' ? env.VITE_BFF_BASE_URL.trim() : ''; + + if (!configuredBaseUrl) { + return path; + } + + return `${configuredBaseUrl.replace(/\/$/, '')}${path}`; +}; + +export const parseBffErrorResponse = async (response: Response): Promise => { + let message = `BFF request failed with status ${response.status}.`; + let parsedCode: string | undefined; + let parsedRetryable: boolean | undefined; + + try { + const text = await response.text(); + if (text) { + const parsed = JSON.parse(text); + const errorPayload = parsed?.error as BffErrorPayload | undefined; + if (errorPayload?.message) { + message = errorPayload.message; + } + parsedCode = errorPayload?.code; + parsedRetryable = errorPayload?.retryable; + } + } catch { + // fallback message stays + } + + const error = new Error(message); + (error as any).status = response.status; + if (parsedCode) (error as any).code = parsedCode; + if (typeof parsedRetryable === 'boolean') (error as any).retryable = parsedRetryable; + return error; +}; + +export const fetchBffJson = async ( + path: string, + init: RequestInit, + signal?: AbortSignal +): Promise => { + const response = await fetch(resolveBffEndpoint(path), { + ...init, + signal: signal || init.signal, + }); + + if (!response.ok) { + throw await parseBffErrorResponse(response); + } + + return (await response.json()) as T; +}; diff --git a/all-model-chat/apps/web/services/api/chatApi.ts b/all-model-chat/apps/web/services/api/chatApi.ts new file mode 100644 index 00000000..9d26bfaf --- /dev/null +++ b/all-model-chat/apps/web/services/api/chatApi.ts @@ -0,0 +1,349 @@ + +import { GenerateContentResponse, Part, UsageMetadata, ChatHistoryItem } from "@google/genai"; +import type { + BffErrorPayload as BffStreamErrorPayload, + ChatStreamMetaEventPayload, + ChatStreamRequestPayload, +} from '@all-model-chat/shared-api'; +import { ThoughtSupportingPart } from '../../types'; +import { logService } from "../logService"; +import { getConfiguredApiClient } from "./baseApi"; +import { parseBffErrorResponse, resolveBffEndpoint } from './bffApi'; +import { BACKEND_MANAGED_KEY_SENTINEL } from '../../utils/apiUtils'; + +interface ParsedSseEvent { + eventName: string; + payload: unknown; +} + +const resolveBffStreamEndpoint = (): string => resolveBffEndpoint('/api/chat/stream'); + +const parseSseEventBlock = (rawBlock: string): ParsedSseEvent | null => { + const normalized = rawBlock.replace(/\r\n/g, '\n').trim(); + if (!normalized) return null; + + let eventName = 'message'; + const dataLines: string[] = []; + + for (const line of normalized.split('\n')) { + if (line.startsWith('event:')) { + eventName = line.slice(6).trim(); + continue; + } + + if (line.startsWith('data:')) { + dataLines.push(line.slice(5).trimStart()); + } + } + + if (dataLines.length === 0) return null; + + const rawPayload = dataLines.join('\n'); + try { + return { + eventName, + payload: JSON.parse(rawPayload), + }; + } catch { + return { + eventName, + payload: rawPayload, + }; + } +}; + +const consumeSseStream = async ( + response: Response, + abortSignal: AbortSignal, + onEvent: (event: ParsedSseEvent) => void +): Promise => { + if (!response.body) { + throw new Error('BFF stream response body is empty.'); + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ''; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + if (abortSignal.aborted) break; + + buffer += decoder.decode(value, { stream: true }); + + while (true) { + const separatorIndex = buffer.indexOf('\n\n'); + if (separatorIndex < 0) break; + + const rawBlock = buffer.slice(0, separatorIndex); + buffer = buffer.slice(separatorIndex + 2); + + const parsed = parseSseEventBlock(rawBlock); + if (parsed) { + onEvent(parsed); + } + } + } + + if (buffer.trim().length > 0) { + const parsed = parseSseEventBlock(buffer); + if (parsed) { + onEvent(parsed); + } + } +}; + +const createBffStreamError = (payload: BffStreamErrorPayload | null | undefined): Error => { + const message = payload?.message || 'BFF stream proxy returned an error.'; + const error = new Error(message); + (error as any).code = payload?.code || 'bff_stream_error'; + (error as any).status = payload?.status; + (error as any).retryable = payload?.retryable; + return error; +}; + +const normalizeThoughtSignaturePart = (part: Part): Part => { + const anyPart = part as any; + const thoughtSignature = + anyPart.thoughtSignature || + anyPart.thought_signature || + anyPart.functionCall?.thoughtSignature || + anyPart.functionCall?.thought_signature; + + if (!thoughtSignature) return part; + + return { + ...part, + thoughtSignature, + // Preserve snake_case to maximize compatibility with Vertex API serialization + thought_signature: thoughtSignature, + } as any; +}; + +/** + * Shared helper to parse GenAI responses. + * Extracts parts, separates thoughts, and merges metadata/citations from tool calls. + */ +const processResponse = (response: GenerateContentResponse) => { + let thoughtsText = ""; + const responseParts: Part[] = []; + + if (response.candidates && response.candidates[0]?.content?.parts) { + for (const part of response.candidates[0].content.parts) { + const pAsThoughtSupporting = part as ThoughtSupportingPart; + if (pAsThoughtSupporting.thought) { + thoughtsText += part.text; + } else { + responseParts.push(part); + } + } + } + + if (responseParts.length === 0 && response.text) { + responseParts.push({ text: response.text }); + } + + const candidate = response.candidates?.[0]; + const groundingMetadata = candidate?.groundingMetadata; + const finalMetadata: any = groundingMetadata ? { ...groundingMetadata } : {}; + + // @ts-ignore - Handle potential snake_case from raw API responses + const urlContextMetadata = candidate?.urlContextMetadata || candidate?.url_context_metadata; + + const toolCalls = candidate?.toolCalls; + if (toolCalls) { + for (const toolCall of toolCalls) { + if (toolCall.functionCall?.args?.urlContextMetadata) { + if (!finalMetadata.citations) finalMetadata.citations = []; + const newCitations = toolCall.functionCall.args.urlContextMetadata.citations || []; + for (const newCitation of newCitations) { + if (!finalMetadata.citations.some((c: any) => c.uri === newCitation.uri)) { + finalMetadata.citations.push(newCitation); + } + } + } + } + } + + return { + parts: responseParts, + thoughts: thoughtsText || undefined, + usage: response.usageMetadata, + grounding: Object.keys(finalMetadata).length > 0 ? finalMetadata : undefined, + urlContext: urlContextMetadata + }; +}; + +export const sendStatelessMessageStreamApi = async ( + apiKey: string, + modelId: string, + history: ChatHistoryItem[], + parts: Part[], + config: any, + abortSignal: AbortSignal, + onPart: (part: Part) => void, + onThoughtChunk: (chunk: string) => void, + onError: (error: Error) => void, + onComplete: (usageMetadata?: UsageMetadata, groundingMetadata?: any, urlContextMetadata?: any, functionCallPart?: Part) => void, + role: 'user' | 'model' = 'user' +): Promise => { + logService.info(`Sending message via BFF /api/chat/stream for ${modelId} (Role: ${role})`); + let finalUsageMetadata: UsageMetadata | undefined = undefined; + let finalGroundingMetadata: any = null; + let finalUrlContextMetadata: any = null; + let detectedFunctionCallPart: Part | undefined = undefined; + + try { + if (abortSignal.aborted) { + logService.warn("Streaming aborted by signal before start."); + return; + } + + const endpoint = resolveBffStreamEndpoint(); + const requestPayload: ChatStreamRequestPayload = { + model: modelId, + history, + parts, + config, + role, + apiKeyOverride: apiKey !== BACKEND_MANAGED_KEY_SENTINEL ? apiKey : undefined, + }; + + const response = await fetch(endpoint, { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + signal: abortSignal, + body: JSON.stringify(requestPayload), + }); + + if (!response.ok) { + throw await parseBffErrorResponse(response); + } + + await consumeSseStream(response, abortSignal, (event) => { + const payload = event.payload as any; + + if (event.eventName === 'meta') { + const metaPayload = payload as ChatStreamMetaEventPayload | undefined; + if (typeof metaPayload?.keyId === 'string') { + logService.recordApiKeyUsage(metaPayload.keyId, { source: 'server' }); + } + return; + } + + if (event.eventName === 'part') { + if (payload?.part) { + onPart(payload.part as Part); + } + return; + } + + if (event.eventName === 'thought') { + if (typeof payload?.chunk === 'string') { + onThoughtChunk(payload.chunk); + } + return; + } + + if (event.eventName === 'complete') { + if (payload?.usageMetadata) { + finalUsageMetadata = payload.usageMetadata as UsageMetadata; + } + if (payload?.groundingMetadata) { + finalGroundingMetadata = payload.groundingMetadata; + } + if (payload?.urlContextMetadata) { + finalUrlContextMetadata = payload.urlContextMetadata; + } + if (payload?.functionCallPart) { + detectedFunctionCallPart = normalizeThoughtSignaturePart(payload.functionCallPart as Part); + } + return; + } + + if (event.eventName === 'error') { + throw createBffStreamError(payload?.error as BffStreamErrorPayload | undefined); + } + }); + } catch (error) { + const isAborted = abortSignal.aborted || (error instanceof Error && error.name === 'AbortError'); + if (isAborted) { + logService.warn("Streaming aborted by signal."); + return; + } + + logService.error("Error sending message (stream):", error); + onError(error instanceof Error ? error : new Error(String(error) || "Unknown error during streaming.")); + } finally { + logService.info("Streaming complete.", { usage: finalUsageMetadata, hasGrounding: !!finalGroundingMetadata, hasFunctionCall: !!detectedFunctionCallPart }); + onComplete(finalUsageMetadata, finalGroundingMetadata, finalUrlContextMetadata, detectedFunctionCallPart); + } +}; + +export const sendStatelessMessageNonStreamApi = async ( + apiKey: string, + modelId: string, + history: ChatHistoryItem[], + parts: Part[], + config: any, + abortSignal: AbortSignal, + onError: (error: Error) => void, + onComplete: (parts: Part[], thoughtsText?: string, usageMetadata?: UsageMetadata, groundingMetadata?: any, urlContextMetadata?: any) => void +): Promise => { + logService.info(`Sending message via stateless generateContent (non-stream) for model ${modelId}`); + + try { + // In backend-managed mode we still need BFF transport, even when UI streaming is disabled. + if (apiKey === BACKEND_MANAGED_KEY_SENTINEL) { + const bufferedParts: Part[] = []; + let bufferedThoughts = ''; + + await sendStatelessMessageStreamApi( + apiKey, + modelId, + history, + parts, + config, + abortSignal, + (part) => bufferedParts.push(part), + (chunk) => { + bufferedThoughts += chunk; + }, + onError, + (usageMetadata, groundingMetadata, urlContextMetadata) => { + onComplete( + bufferedParts, + bufferedThoughts || undefined, + usageMetadata, + groundingMetadata, + urlContextMetadata + ); + } + ); + return; + } + + const ai = await getConfiguredApiClient(apiKey); + + if (abortSignal.aborted) { onComplete([], "", undefined, undefined, undefined); return; } + + const response = await ai.models.generateContent({ + model: modelId, + contents: [...history, { role: 'user', parts }], + config: config + }); + + if (abortSignal.aborted) { onComplete([], "", undefined, undefined, undefined); return; } + + const { parts: responseParts, thoughts, usage, grounding, urlContext } = processResponse(response); + + logService.info(`Stateless non-stream complete for ${modelId}.`, { usage, hasGrounding: !!grounding, hasUrlContext: !!urlContext }); + onComplete(responseParts, thoughts, usage, grounding, urlContext); + } catch (error) { + logService.error(`Error in stateless non-stream for ${modelId}:`, error); + onError(error instanceof Error ? error : new Error(String(error) || "Unknown error during stateless non-streaming call.")); + } +}; diff --git a/all-model-chat/services/api/fileApi.ts b/all-model-chat/apps/web/services/api/fileApi.ts similarity index 60% rename from all-model-chat/services/api/fileApi.ts rename to all-model-chat/apps/web/services/api/fileApi.ts index 7b824a8e..273d3366 100644 --- a/all-model-chat/services/api/fileApi.ts +++ b/all-model-chat/apps/web/services/api/fileApi.ts @@ -1,6 +1,7 @@ import { File as GeminiFile } from "@google/genai"; -import { getConfiguredApiClient } from './baseApi'; +import type { FileMetadataResponse, FileUploadResponse } from '@all-model-chat/shared-api'; +import { fetchBffJson, parseBffErrorResponse, resolveBffEndpoint } from './bffApi'; import { logService } from "../logService"; /** @@ -15,7 +16,8 @@ export const uploadFileApi = async ( signal: AbortSignal, onProgress?: (loaded: number, total: number) => void ): Promise => { - logService.info(`Uploading file (SDK): ${displayName}`, { mimeType, size: file.size }); + void apiKey; + logService.info(`Uploading file via BFF: ${displayName}`, { mimeType, size: file.size }); if (signal.aborted) { const abortError = new Error("Upload cancelled by user."); @@ -24,18 +26,29 @@ export const uploadFileApi = async ( } try { - // Get configured SDK client (handles proxy settings automatically) - const ai = await getConfiguredApiClient(apiKey); + const params = new URLSearchParams({ + displayName, + mimeType, + }); - // Use official SDK for upload, which is more stable for Auth and CORS - const uploadResult = await ai.files.upload({ - file: file, - config: { - displayName: displayName, - mimeType: mimeType, + const endpoint = resolveBffEndpoint(`/api/files/upload?${params.toString()}`); + const binaryBody = await file.arrayBuffer(); + const response = await fetch(endpoint, { + method: 'POST', + headers: { + 'content-type': 'application/octet-stream', }, + body: binaryBody, + signal, }); + if (!response.ok) { + throw await parseBffErrorResponse(response); + } + + const payload = (await response.json()) as FileUploadResponse; + const uploadResult = payload.file; + // Since SDK doesn't provide progress, call 100% on completion to satisfy UI if (onProgress) { onProgress(file.size, file.size); @@ -58,17 +71,23 @@ export const uploadFileApi = async ( }; export const getFileMetadataApi = async (apiKey: string, fileApiName: string): Promise => { + void apiKey; if (!fileApiName || !fileApiName.startsWith('files/')) { logService.error(`Invalid fileApiName format: ${fileApiName}. Must start with "files/".`); throw new Error('Invalid file ID format. Expected "files/your_file_id".'); } try { logService.info(`Fetching metadata for file: ${fileApiName}`); - const ai = await getConfiguredApiClient(apiKey); - const file = await ai.files.get({ name: fileApiName }); - return file; + const params = new URLSearchParams({ name: fileApiName }); + const payload = await fetchBffJson>( + `/api/files/metadata?${params.toString()}`, + { + method: 'GET', + } + ); + return payload.file; } catch (error) { - logService.error(`Failed to get metadata for file "${fileApiName}" from Gemini API:`, error); + logService.error(`Failed to get metadata for file "${fileApiName}" from BFF:`, error); if (error instanceof Error && (error.message.includes('NOT_FOUND') || error.message.includes('404'))) { return null; // File not found is a valid outcome we want to handle } diff --git a/all-model-chat/apps/web/services/api/generation/audioApi.ts b/all-model-chat/apps/web/services/api/generation/audioApi.ts new file mode 100644 index 00000000..38057ca4 --- /dev/null +++ b/all-model-chat/apps/web/services/api/generation/audioApi.ts @@ -0,0 +1,91 @@ + +import type { + SpeechGenerationRequest, + SpeechGenerationResponse, + TranscribeAudioRequest, + TranscribeAudioResponse, +} from '@all-model-chat/shared-api'; +import { fetchBffJson } from '../bffApi'; +import { logService } from "../../logService"; +import { blobToBase64 } from "../../../utils/appUtils"; + +export const generateSpeechApi = async (apiKey: string, modelId: string, text: string, voice: string, abortSignal: AbortSignal): Promise => { + void apiKey; + logService.info(`Generating speech with model ${modelId}`, { textLength: text.length, voice }); + + if (!text.trim()) { + throw new Error("TTS input text cannot be empty."); + } + + try { + const requestPayload: SpeechGenerationRequest = { + model: modelId, + text, + voice, + }; + + const response = await fetchBffJson( + '/api/generation/speech', + { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify(requestPayload), + }, + abortSignal + ); + + if (abortSignal.aborted) { + const abortError = new Error("Speech generation cancelled by user."); + abortError.name = "AbortError"; + throw abortError; + } + + const audioData = response.audioData; + + if (typeof audioData === 'string' && audioData.length > 0) { + return audioData; + } + + throw new Error('No audio data found in TTS response.'); + + } catch (error) { + logService.error(`Failed to generate speech with model ${modelId}:`, error); + throw error; + } +}; + +export const transcribeAudioApi = async (apiKey: string, audioFile: File, modelId: string): Promise => { + void apiKey; + logService.info(`Transcribing audio with model ${modelId}`, { fileName: audioFile.name, size: audioFile.size }); + + try { + // Use blobToBase64 which is efficient and handles Blobs/Files + const audioBase64 = await blobToBase64(audioFile); + const requestPayload: TranscribeAudioRequest = { + model: modelId, + mimeType: audioFile.type, + audioBase64, + }; + const response = await fetchBffJson( + '/api/generation/transcribe', + { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify(requestPayload), + } + ); + + if (response.text) { + return response.text; + } else { + throw new Error("Transcription failed. The model returned an empty response."); + } + } catch (error) { + logService.error("Error during audio transcription:", error); + throw error; + } +}; diff --git a/all-model-chat/apps/web/services/api/generation/imageApi.ts b/all-model-chat/apps/web/services/api/generation/imageApi.ts new file mode 100644 index 00000000..2946e458 --- /dev/null +++ b/all-model-chat/apps/web/services/api/generation/imageApi.ts @@ -0,0 +1,96 @@ +import { ChatHistoryItem, Part } from '@google/genai'; +import type { + EditImageRequest, + EditImageResponse, + ImageGenerationRequest, + ImageGenerationResponse, +} from '@all-model-chat/shared-api'; +import { fetchBffJson } from '../bffApi'; +import { logService } from "../../logService"; + +export const generateImagesApi = async (apiKey: string, modelId: string, prompt: string, aspectRatio: string, imageSize: string | undefined, abortSignal: AbortSignal): Promise => { + void apiKey; + logService.info(`Generating image with model ${modelId}`, { prompt, aspectRatio, imageSize }); + + if (!prompt.trim()) { + throw new Error("Image generation prompt cannot be empty."); + } + + if (abortSignal.aborted) { + const abortError = new Error("Image generation cancelled by user before starting."); + abortError.name = "AbortError"; + throw abortError; + } + + try { + const requestPayload: ImageGenerationRequest = { + model: modelId, + prompt, + aspectRatio, + imageSize, + }; + + const response = await fetchBffJson( + '/api/generation/images', + { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify(requestPayload), + }, + abortSignal + ); + + if (abortSignal.aborted) { + const abortError = new Error("Image generation cancelled by user."); + abortError.name = "AbortError"; + throw abortError; + } + + const images = response.images || []; + if (images.length === 0) { + throw new Error("No images generated. The prompt may have been blocked or the model failed to respond."); + } + + return images; + + } catch (error) { + logService.error(`Failed to generate images with model ${modelId}:`, error); + throw error; + } +}; + +export const editImageApi = async ( + apiKey: string, + modelId: string, + history: ChatHistoryItem[], + parts: Part[], + abortSignal: AbortSignal, + aspectRatio?: string, + imageSize?: string +): Promise => { + void apiKey; + + const requestPayload: EditImageRequest = { + model: modelId, + history, + parts, + aspectRatio, + imageSize, + }; + + const response = await fetchBffJson( + '/api/generation/edit-image', + { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify(requestPayload), + }, + abortSignal + ); + + return response.parts || []; +}; diff --git a/all-model-chat/apps/web/services/api/generation/textApi.ts b/all-model-chat/apps/web/services/api/generation/textApi.ts new file mode 100644 index 00000000..45a65020 --- /dev/null +++ b/all-model-chat/apps/web/services/api/generation/textApi.ts @@ -0,0 +1,101 @@ +import type { + SuggestionsRequest, + SuggestionsResponse, + TitleRequest, + TitleResponse, + TranslateRequest, + TranslateResponse, +} from '@all-model-chat/shared-api'; +import { fetchBffJson } from '../bffApi'; +import { logService } from "../../logService"; + +export const translateTextApi = async (apiKey: string, text: string, targetLanguage: string = 'English'): Promise => { + void apiKey; + logService.info(`Translating text to ${targetLanguage}...`); + + try { + const requestPayload: TranslateRequest = { + text, + targetLanguage, + }; + const response = await fetchBffJson( + '/api/generation/translate', + { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify(requestPayload), + } + ); + + if (response.text) { + return response.text; + } else { + throw new Error("Translation failed. The model returned an empty response."); + } + } catch (error) { + logService.error("Error during text translation:", error); + throw error; + } +}; + +export const generateSuggestionsApi = async (apiKey: string, userContent: string, modelContent: string, language: 'en' | 'zh'): Promise => { + void apiKey; + logService.info(`Generating suggestions in ${language}...`); + + try { + const requestPayload: SuggestionsRequest = { + userContent, + modelContent, + language, + }; + const response = await fetchBffJson( + '/api/generation/suggestions', + { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify(requestPayload), + } + ); + + return (response.suggestions || []).slice(0, 3); + } catch (error) { + logService.error("Error during suggestions generation:", error); + return []; // Return empty array on failure + } +}; + +export const generateTitleApi = async (apiKey: string, userContent: string, modelContent: string, language: 'en' | 'zh'): Promise => { + void apiKey; + logService.info(`Generating title in ${language}...`); + + try { + const requestPayload: TitleRequest = { + userContent, + modelContent, + language, + }; + const response = await fetchBffJson( + '/api/generation/title', + { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify(requestPayload), + } + ); + + if (response.title) { + return response.title; + } else { + throw new Error("Title generation failed. The model returned an empty response."); + } + } catch (error) { + logService.error("Error during title generation:", error); + throw error; + } +}; diff --git a/all-model-chat/apps/web/services/api/generation/tokenApi.ts b/all-model-chat/apps/web/services/api/generation/tokenApi.ts new file mode 100644 index 00000000..28af0326 --- /dev/null +++ b/all-model-chat/apps/web/services/api/generation/tokenApi.ts @@ -0,0 +1,30 @@ + +import { fetchBffJson } from '../bffApi'; +import { logService } from "../../logService"; +import { Part } from "@google/genai"; +import type { CountTokensRequest, CountTokensResponse } from '@all-model-chat/shared-api'; + +export const countTokensApi = async (apiKey: string, modelId: string, parts: Part[]): Promise => { + void apiKey; + logService.info(`Counting tokens for model ${modelId}...`); + try { + const requestPayload: CountTokensRequest = { + model: modelId, + parts, + }; + const response = await fetchBffJson( + '/api/generation/count-tokens', + { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify(requestPayload), + } + ); + return response.totalTokens || 0; + } catch (error) { + logService.error("Error counting tokens:", error); + throw error; + } +}; diff --git a/all-model-chat/services/api/generationApi.ts b/all-model-chat/apps/web/services/api/generationApi.ts similarity index 100% rename from all-model-chat/services/api/generationApi.ts rename to all-model-chat/apps/web/services/api/generationApi.ts diff --git a/all-model-chat/services/geminiService.ts b/all-model-chat/apps/web/services/geminiService.ts similarity index 68% rename from all-model-chat/services/geminiService.ts rename to all-model-chat/apps/web/services/geminiService.ts index d57b599f..ef72e010 100644 --- a/all-model-chat/services/geminiService.ts +++ b/all-model-chat/apps/web/services/geminiService.ts @@ -1,8 +1,8 @@ import { GeminiService, ModelOption } from '../types'; -import { Part, UsageMetadata, File as GeminiFile, ChatHistoryItem, Modality } from "@google/genai"; +import { Part, UsageMetadata, File as GeminiFile, ChatHistoryItem } from "@google/genai"; import { uploadFileApi, getFileMetadataApi } from './api/fileApi'; -import { generateImagesApi, generateSpeechApi, transcribeAudioApi, translateTextApi, generateTitleApi, generateSuggestionsApi, countTokensApi } from './api/generationApi'; +import { generateImagesApi, editImageApi, generateSpeechApi, transcribeAudioApi, translateTextApi, generateTitleApi, generateSuggestionsApi, countTokensApi } from './api/generationApi'; import { sendStatelessMessageStreamApi, sendStatelessMessageNonStreamApi } from './api/chatApi'; import { logService } from "./logService"; @@ -12,16 +12,16 @@ class GeminiServiceImpl implements GeminiService { } async uploadFile( - apiKey: string, - file: File, - mimeType: string, - displayName: string, + apiKey: string, + file: File, + mimeType: string, + displayName: string, signal: AbortSignal, onProgress?: (loaded: number, total: number) => void ): Promise { return uploadFileApi(apiKey, file, mimeType, displayName, signal, onProgress); } - + async getFileMetadata(apiKey: string, fileApiName: string): Promise { return getFileMetadataApi(apiKey, fileApiName); } @@ -55,44 +55,7 @@ class GeminiServiceImpl implements GeminiService { } async editImage(apiKey: string, modelId: string, history: ChatHistoryItem[], parts: Part[], abortSignal: AbortSignal, aspectRatio?: string, imageSize?: string): Promise { - return new Promise((resolve, reject) => { - if (abortSignal.aborted) { - const abortError = new Error("aborted"); - abortError.name = "AbortError"; - return reject(abortError); - } - const handleComplete = (responseParts: Part[]) => { - resolve(responseParts); - }; - const handleError = (error: Error) => { - reject(error); - }; - - const config: any = { - responseModalities: [Modality.IMAGE, Modality.TEXT], - }; - - if (aspectRatio && aspectRatio !== 'Auto') { - if (!config.imageConfig) config.imageConfig = {}; - config.imageConfig.aspectRatio = aspectRatio; - } - - if (modelId === 'gemini-3-pro-image-preview' && imageSize) { - if (!config.imageConfig) config.imageConfig = {}; - config.imageConfig.imageSize = imageSize; - } - - sendStatelessMessageNonStreamApi( - apiKey, - modelId, - history, - parts, - config, - abortSignal, - handleError, - (responseParts, thoughts, usage, grounding) => handleComplete(responseParts) - ); - }); + return editImageApi(apiKey, modelId, history, parts, abortSignal, aspectRatio, imageSize); } async sendMessageStream( @@ -105,7 +68,7 @@ class GeminiServiceImpl implements GeminiService { onPart: (part: Part) => void, onThoughtChunk: (chunk: string) => void, onError: (error: Error) => void, - onComplete: (usageMetadata?: UsageMetadata, groundingMetadata?: any, urlContextMetadata?: any) => void, + onComplete: (usageMetadata?: UsageMetadata, groundingMetadata?: any, urlContextMetadata?: any, functionCallPart?: Part) => void, role: 'user' | 'model' = 'user' ): Promise { return sendStatelessMessageStreamApi( diff --git a/all-model-chat/services/logService.ts b/all-model-chat/apps/web/services/logService.ts similarity index 80% rename from all-model-chat/services/logService.ts rename to all-model-chat/apps/web/services/logService.ts index 6bd84dca..ac4b6fa4 100644 --- a/all-model-chat/services/logService.ts +++ b/all-model-chat/apps/web/services/logService.ts @@ -21,8 +21,10 @@ export interface TokenUsageStats { type LogListener = (newLogs: LogEntry[]) => void; type ApiKeyListener = (usage: Map) => void; type TokenUsageListener = (usage: Map) => void; +type ApiKeyUsageSource = 'server' | 'client'; -const API_USAGE_STORAGE_KEY = 'chatApiUsageData'; +const API_USAGE_STORAGE_KEY = 'chatApiUsageDataV2'; +const LEGACY_API_USAGE_STORAGE_KEY = 'chatApiUsageData'; const TOKEN_USAGE_STORAGE_KEY = 'chatTokenUsageData'; const LOG_RETENTION_MS = 2 * 24 * 60 * 60 * 1000; // 2 days const FLUSH_INTERVAL_MS = 2000; // 2 seconds @@ -130,15 +132,46 @@ class LogServiceImpl { // --- API Usage Tracking (Kept in LocalStorage for speed/simplicity) --- + private normalizeApiUsageKeyId(keyId: string, source: ApiKeyUsageSource): string | null { + const normalizedKeyId = keyId.trim(); + if (!normalizedKeyId) return null; + if (!/^[a-zA-Z0-9._-]+$/.test(normalizedKeyId)) return null; + return `${source}:${normalizedKeyId}`; + } + + private parseStoredApiUsageKey(rawKey: unknown): string | null { + if (typeof rawKey !== 'string') return null; + const trimmed = rawKey.trim(); + if (!trimmed) return null; + const separatorIndex = trimmed.indexOf(':'); + if (separatorIndex <= 0) return null; + const source = trimmed.slice(0, separatorIndex); + const keyId = trimmed.slice(separatorIndex + 1); + if (source !== 'server' && source !== 'client') return null; + return this.normalizeApiUsageKeyId(keyId, source); + } + private loadApiKeyUsage() { try { const storedUsage = localStorage.getItem(API_USAGE_STORAGE_KEY); if (storedUsage) { const parsed = JSON.parse(storedUsage); if (Array.isArray(parsed)) { - this.apiKeyUsage = new Map(parsed); + const sanitizedUsage = new Map(); + for (const entry of parsed) { + if (!Array.isArray(entry) || entry.length < 2) continue; + const normalizedKeyId = this.parseStoredApiUsageKey(entry[0]); + const count = Number(entry[1]); + if (!normalizedKeyId || !Number.isFinite(count) || count <= 0) continue; + sanitizedUsage.set(normalizedKeyId, (sanitizedUsage.get(normalizedKeyId) || 0) + Math.floor(count)); + } + this.apiKeyUsage = sanitizedUsage; + localStorage.setItem(API_USAGE_STORAGE_KEY, JSON.stringify(Array.from(sanitizedUsage.entries()))); } } + + // Legacy data may contain raw keys. Purge it after v2 usage map is loaded. + localStorage.removeItem(LEGACY_API_USAGE_STORAGE_KEY); } catch (e) { console.error("Failed to load API key usage:", e); } @@ -248,10 +281,12 @@ class LogServiceImpl { return error; } - public recordApiKeyUsage(apiKey: string) { - if (!apiKey) return; - const currentCount = this.apiKeyUsage.get(apiKey) || 0; - this.apiKeyUsage.set(apiKey, currentCount + 1); + public recordApiKeyUsage(keyId: string, options: { source?: ApiKeyUsageSource } = {}) { + const source = options.source || 'server'; + const usageKeyId = this.normalizeApiUsageKeyId(keyId, source); + if (!usageKeyId) return; + const currentCount = this.apiKeyUsage.get(usageKeyId) || 0; + this.apiKeyUsage.set(usageKeyId, currentCount + 1); this.saveApiKeyUsage(); this.notifyApiKeyListeners(); } diff --git a/all-model-chat/services/networkInterceptor.ts b/all-model-chat/apps/web/services/networkInterceptor.ts similarity index 100% rename from all-model-chat/services/networkInterceptor.ts rename to all-model-chat/apps/web/services/networkInterceptor.ts diff --git a/all-model-chat/services/streamingStore.ts b/all-model-chat/apps/web/services/streamingStore.ts similarity index 100% rename from all-model-chat/services/streamingStore.ts rename to all-model-chat/apps/web/services/streamingStore.ts diff --git a/all-model-chat/styles/animations.css b/all-model-chat/apps/web/styles/animations.css similarity index 100% rename from all-model-chat/styles/animations.css rename to all-model-chat/apps/web/styles/animations.css diff --git a/all-model-chat/styles/main.css b/all-model-chat/apps/web/styles/main.css similarity index 100% rename from all-model-chat/styles/main.css rename to all-model-chat/apps/web/styles/main.css diff --git a/all-model-chat/styles/markdown.css b/all-model-chat/apps/web/styles/markdown.css similarity index 100% rename from all-model-chat/styles/markdown.css rename to all-model-chat/apps/web/styles/markdown.css diff --git a/all-model-chat/sw.js b/all-model-chat/apps/web/sw.js similarity index 100% rename from all-model-chat/sw.js rename to all-model-chat/apps/web/sw.js diff --git a/all-model-chat/apps/web/tsconfig.json b/all-model-chat/apps/web/tsconfig.json new file mode 100644 index 00000000..c5c08fe6 --- /dev/null +++ b/all-model-chat/apps/web/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "paths": { + "@all-model-chat/shared-api": [ + "../../packages/shared-api/src/index.d.ts" + ], + "@all-model-chat/shared-types": [ + "../../packages/shared-types/src/index.d.ts" + ], + "@/*": [ + "./*" + ] + } + } +} diff --git a/all-model-chat/types.ts b/all-model-chat/apps/web/types.ts similarity index 100% rename from all-model-chat/types.ts rename to all-model-chat/apps/web/types.ts diff --git a/all-model-chat/types/api.ts b/all-model-chat/apps/web/types/api.ts similarity index 92% rename from all-model-chat/types/api.ts rename to all-model-chat/apps/web/types/api.ts index f92a00dd..bc75e8d6 100644 --- a/all-model-chat/types/api.ts +++ b/all-model-chat/apps/web/types/api.ts @@ -4,15 +4,15 @@ import { ModelOption } from './settings'; export interface GeminiService { uploadFile: ( - apiKey: string, - file: File, - mimeType: string, - displayName: string, + apiKey: string, + file: File, + mimeType: string, + displayName: string, signal: AbortSignal, onProgress?: (loaded: number, total: number) => void ) => Promise; getFileMetadata: (apiKey: string, fileApiName: string) => Promise; - + // Stateless Message Sending sendMessageStream: ( apiKey: string, @@ -24,7 +24,7 @@ export interface GeminiService { onPart: (part: Part) => void, onThoughtChunk: (chunk: string) => void, onError: (error: Error) => void, - onComplete: (usageMetadata?: UsageMetadata, groundingMetadata?: any, urlContextMetadata?: any) => void, + onComplete: (usageMetadata?: UsageMetadata, groundingMetadata?: any, urlContextMetadata?: any, functionCallPart?: Part) => void, role?: 'user' | 'model' ) => Promise; @@ -50,5 +50,5 @@ export interface GeminiService { } export interface ThoughtSupportingPart extends Part { - thought?: any; + thought?: any; } diff --git a/all-model-chat/types/chat.ts b/all-model-chat/apps/web/types/chat.ts similarity index 83% rename from all-model-chat/types/chat.ts rename to all-model-chat/apps/web/types/chat.ts index 32b92244..4326149e 100644 --- a/all-model-chat/types/chat.ts +++ b/all-model-chat/apps/web/types/chat.ts @@ -11,6 +11,56 @@ export interface VideoMetadata { fps?: number; } +// ============ Project Context Types (Agentic Folder Access) ============ +// These types support on-demand file reading via function calling + +/** Represents a node in the file tree structure */ +export interface FileTreeNode { + name: string; + path: string; // Relative path from root + isDirectory: boolean; + size?: number; + children?: FileTreeNode[]; +} + +/** Individual file reference with in-memory handle for lazy reading */ +export interface ProjectContextFile { + path: string; + size: number; + /** In-memory File object - lost on page refresh unless using File System Access API */ + fileRef: File; + /** Optional: Persistent handle from showDirectoryPicker (survives refresh if stored in IndexedDB) */ + directoryHandle?: FileSystemDirectoryHandle; +} + +/** Complete project context for agentic file access */ +export interface ProjectContext { + /** Root folder name */ + rootName: string; + /** ASCII tree representation for system prompt injection */ + fileTree: string; + /** Structured tree for UI rendering */ + treeNodes: FileTreeNode[]; + /** Map of relative path -> file reference for quick lookup */ + fileMap: Map; + /** Total accessible files count */ + totalFiles: number; + /** Total size in bytes */ + totalSize: number; + /** Whether this context was created via File System Access API (persistent) */ + isPersistent?: boolean; + /** Timestamp when context was created */ + createdAt: Date; +} + +/** State for tracking which files AI has read during conversation */ +export interface ProjectContextReadState { + /** Files that have been read by AI during this conversation */ + readFiles: Set; + /** Currently loading file (for UI feedback) */ + loadingFile: string | null; +} + export interface UploadedFile { id: string; name: string; // Original filename @@ -230,6 +280,7 @@ export interface ChatInputProps { onStopGenerating: () => void; onCancelEdit: () => void; onProcessFiles: (files: FileList | File[]) => Promise; + onProjectContextCreated?: (context: ProjectContext) => void; onAddFileById: (fileId: string) => Promise; onCancelUpload: (fileId: string) => void; onTranscribeAudio: (file: File) => Promise; diff --git a/all-model-chat/types/settings.ts b/all-model-chat/apps/web/types/settings.ts similarity index 57% rename from all-model-chat/types/settings.ts rename to all-model-chat/apps/web/types/settings.ts index 8986e6ef..2ea2e25a 100644 --- a/all-model-chat/types/settings.ts +++ b/all-model-chat/apps/web/types/settings.ts @@ -30,16 +30,16 @@ export enum MediaResolution { } export interface SafetySetting { - category: HarmCategory; - threshold: HarmBlockThreshold; + category: HarmCategory; + threshold: HarmBlockThreshold; } export interface FilesApiConfig { - images: boolean; - pdfs: boolean; - audio: boolean; - video: boolean; - text: boolean; + images: boolean; + pdfs: boolean; + audio: boolean; + video: boolean; + text: boolean; } export interface ChatSettings { @@ -63,33 +63,33 @@ export interface ChatSettings { } export interface AppSettings extends ChatSettings { - themeId: 'system' | 'onyx' | 'pearl'; - baseFontSize: number; - useCustomApiConfig: boolean; - apiKey: string | null; - apiProxyUrl: string | null; - useApiProxy?: boolean; - language: 'en' | 'zh' | 'system'; - isStreamingEnabled: boolean; - transcriptionModelId: string; - filesApiConfig: FilesApiConfig; - expandCodeBlocksByDefault: boolean; - isAutoTitleEnabled: boolean; - isMermaidRenderingEnabled: boolean; - isGraphvizRenderingEnabled?: boolean; - isCompletionNotificationEnabled: boolean; - isCompletionSoundEnabled?: boolean; - isSuggestionsEnabled: boolean; - isAutoScrollOnSendEnabled?: boolean; - isAutoSendOnSuggestionClick?: boolean; - generateQuadImages?: boolean; - autoFullscreenHtml?: boolean; - showWelcomeSuggestions?: boolean; - isAudioCompressionEnabled: boolean; - autoCanvasVisualization?: boolean; - autoCanvasModelId: string; - isPasteRichTextAsMarkdownEnabled?: boolean; - isPasteAsTextFileEnabled?: boolean; - isSystemAudioRecordingEnabled?: boolean; - customShortcuts: Record; // ID -> Key Combination String + themeId: 'system' | 'onyx' | 'pearl'; + baseFontSize: number; + useCustomApiConfig: boolean; + apiKey: string | null; + apiProxyUrl: string | null; + useApiProxy?: boolean; + language: 'en' | 'zh' | 'system'; + isStreamingEnabled: boolean; + transcriptionModelId: string; + filesApiConfig: FilesApiConfig; + expandCodeBlocksByDefault: boolean; + isAutoTitleEnabled: boolean; + isMermaidRenderingEnabled: boolean; + isGraphvizRenderingEnabled?: boolean; + isCompletionNotificationEnabled: boolean; + isCompletionSoundEnabled?: boolean; + isSuggestionsEnabled: boolean; + isAutoScrollOnSendEnabled?: boolean; + isAutoSendOnSuggestionClick?: boolean; + generateQuadImages?: boolean; + autoFullscreenHtml?: boolean; + showWelcomeSuggestions?: boolean; + isAudioCompressionEnabled: boolean; + autoCanvasVisualization?: boolean; + autoCanvasModelId: string; + isPasteRichTextAsMarkdownEnabled?: boolean; + isPasteAsTextFileEnabled?: boolean; + isSystemAudioRecordingEnabled?: boolean; + customShortcuts: Record; // ID -> Key Combination String } diff --git a/all-model-chat/types/theme.ts b/all-model-chat/apps/web/types/theme.ts similarity index 100% rename from all-model-chat/types/theme.ts rename to all-model-chat/apps/web/types/theme.ts diff --git a/all-model-chat/utils/apiUtils.ts b/all-model-chat/apps/web/utils/apiUtils.ts similarity index 92% rename from all-model-chat/utils/apiUtils.ts rename to all-model-chat/apps/web/utils/apiUtils.ts index ca2e9d7f..b39b5ac5 100644 --- a/all-model-chat/utils/apiUtils.ts +++ b/all-model-chat/apps/web/utils/apiUtils.ts @@ -3,6 +3,8 @@ import { AppSettings, ChatSettings } from '../types'; import { API_KEY_LAST_USED_INDEX_KEY } from '../constants/appConstants'; import { logService } from '../services/logService'; +export const BACKEND_MANAGED_KEY_SENTINEL = '__BFF_BACKEND_MANAGED_KEY__'; + export const getActiveApiConfig = (appSettings: AppSettings): { apiKeysString: string | null } => { if (appSettings.useCustomApiConfig) { return { @@ -10,7 +12,8 @@ export const getActiveApiConfig = (appSettings: AppSettings): { apiKeysString: s }; } return { - apiKeysString: process.env.API_KEY || null, + // Backend-managed mode: key material lives on BFF. Keep a sentinel so callers can proceed. + apiKeysString: BACKEND_MANAGED_KEY_SENTINEL, }; }; @@ -33,12 +36,6 @@ export const getKeyForRequest = ( ): { key: string; isNewKey: boolean } | { error: string } => { const { skipIncrement = false } = options; - const logUsage = (key: string) => { - if (appSettings.useCustomApiConfig) { - logService.recordApiKeyUsage(key); - } - }; - const { apiKeysString } = getActiveApiConfig(appSettings); if (!apiKeysString) { return { error: "API Key not configured." }; @@ -57,7 +54,6 @@ export const getKeyForRequest = ( // For environment keys, we assume they are valid if availableKeys contains them or if we just trust the lock. // Here we check if the locked key is in the available list. if (availableKeys.includes(currentChatSettings.lockedApiKey)) { - logUsage(currentChatSettings.lockedApiKey); return { key: currentChatSettings.lockedApiKey, isNewKey: false }; } else { logService.warn(`Locked key not found in current configuration. Falling back to rotation.`); @@ -67,7 +63,6 @@ export const getKeyForRequest = ( if (availableKeys.length === 1) { const key = availableKeys[0]; - logUsage(key); // If we fell through from an invalid locked key, isNewKey should be true to update the session const isNewKey = currentChatSettings.lockedApiKey !== key; return { key, isNewKey }; @@ -105,7 +100,6 @@ export const getKeyForRequest = ( } const nextKey = availableKeys[targetIndex]; - logUsage(nextKey); // If we are here, we are providing a rotated key. // If there was a locked key that was invalid, this is definitely a new key. diff --git a/all-model-chat/utils/appUtils.ts b/all-model-chat/apps/web/utils/appUtils.ts similarity index 100% rename from all-model-chat/utils/appUtils.ts rename to all-model-chat/apps/web/utils/appUtils.ts diff --git a/all-model-chat/utils/audio/audioProcessing.ts b/all-model-chat/apps/web/utils/audio/audioProcessing.ts similarity index 100% rename from all-model-chat/utils/audio/audioProcessing.ts rename to all-model-chat/apps/web/utils/audio/audioProcessing.ts diff --git a/all-model-chat/utils/audio/audioWorklet.ts b/all-model-chat/apps/web/utils/audio/audioWorklet.ts similarity index 100% rename from all-model-chat/utils/audio/audioWorklet.ts rename to all-model-chat/apps/web/utils/audio/audioWorklet.ts diff --git a/all-model-chat/utils/audioCompression.ts b/all-model-chat/apps/web/utils/audioCompression.ts similarity index 100% rename from all-model-chat/utils/audioCompression.ts rename to all-model-chat/apps/web/utils/audioCompression.ts diff --git a/all-model-chat/apps/web/utils/chat/builder.ts b/all-model-chat/apps/web/utils/chat/builder.ts new file mode 100644 index 00000000..3beb48f8 --- /dev/null +++ b/all-model-chat/apps/web/utils/chat/builder.ts @@ -0,0 +1,186 @@ + +import { ChatMessage, ContentPart, UploadedFile, ChatHistoryItem } from '../../types'; +import { SUPPORTED_TEXT_MIME_TYPES, TEXT_BASED_EXTENSIONS } from '../../constants/fileConstants'; +import { logService } from '../../services/logService'; +import { blobToBase64, fileToString } from '../fileHelpers'; +import { isGemini3Model } from '../modelHelpers'; +import { MediaResolution } from '../../types/settings'; + +export const buildContentParts = async ( + text: string, + files: UploadedFile[] | undefined, + modelId?: string, + mediaResolution?: MediaResolution +): Promise<{ + contentParts: ContentPart[]; + enrichedFiles: UploadedFile[]; +}> => { + const filesToProcess = files || []; + + // Check if model supports per-part resolution (Gemini 3 family) + const isGemini3 = modelId && isGemini3Model(modelId); + + const processedResults = await Promise.all(filesToProcess.map(async (file) => { + const newFile = { ...file }; + let part: ContentPart | null = null; + + if (file.isProcessing || file.error || file.uploadState !== 'active') { + return { file: newFile, part }; + } + + const isVideo = file.type.startsWith('video/'); + const isYoutube = file.type === 'video/youtube-link'; + // Check if file should be treated as text content (not base64 inlineData) + const fileExtension = `.${file.name.split('.').pop()?.toLowerCase()}`; + const isTextLike = SUPPORTED_TEXT_MIME_TYPES.includes(file.type) || TEXT_BASED_EXTENSIONS.includes(fileExtension) || file.type === 'text/plain'; + + if (file.fileUri) { + // 1. Files uploaded via API (or YouTube links) + if (isYoutube) { + // YouTube links need mimeType for Vertex AI compatibility + part = { fileData: { mimeType: 'video/*', fileUri: file.fileUri } }; + } else { + part = { fileData: { mimeType: file.type, fileUri: file.fileUri } }; + } + } else { + // 2. Files NOT uploaded via API (Inline handling) + const fileSource = file.rawFile; + const urlSource = file.dataUrl; + + if (isTextLike) { + // Special handling for text/code: Read content and wrap in text part + let textContent = ''; + if (fileSource && (fileSource instanceof File || fileSource instanceof Blob)) { + textContent = await fileToString(fileSource as File); + } else if (urlSource) { + const response = await fetch(urlSource); + textContent = await response.text(); + } + if (textContent) { + part = { text: `\n--- START OF FILE ${file.name} ---\n${textContent}\n--- END OF FILE ${file.name} ---\n` }; + } + } else { + // Standard Inline Data (Images, PDFs, Audio, Video if small enough) + // STRICT ALLOWLIST for Inline Data to prevent API 400 errors (e.g. for .xlsx) + const isSupportedInlineType = + file.type.startsWith('image/') || + file.type.startsWith('audio/') || + file.type.startsWith('video/') || + file.type === 'application/pdf'; + + if (isSupportedInlineType) { + let base64DataForApi: string | undefined; + + if (fileSource && (fileSource instanceof Blob || fileSource instanceof File)) { + try { + base64DataForApi = await blobToBase64(fileSource); + } catch (error) { + logService.error(`Failed to convert rawFile to base64 for ${file.name}`, { error }); + } + } else if (urlSource) { + try { + const response = await fetch(urlSource); + const blob = await response.blob(); + base64DataForApi = await blobToBase64(blob); + + // Self-repair: If we had to fetch because rawFile was missing, recreate it + if (!newFile.rawFile) { + newFile.rawFile = new File([blob], file.name, { type: file.type }); + } + } catch (error) { + logService.error(`Failed to fetch blob and convert to base64 for ${file.name}`, { error }); + } + } + + if (base64DataForApi) { + part = { inlineData: { mimeType: file.type, data: base64DataForApi } }; + } + } else { + // Fallback for unsupported binary types that aren't text-readable (e.g. Excel, Zip without extraction) + // This prevents the API from rejecting the request with "Unsupported MIME type" + part = { text: `[Attachment: ${file.name} (Binary content not supported for direct reading)]` }; + } + } + } + + // Inject video metadata if present and it's a video (works for both inline and fileUri video/youtube) + if (part && (isVideo || isYoutube) && file.videoMetadata) { + part.videoMetadata = { ...part.videoMetadata }; // Ensure object exists + + if (file.videoMetadata.startOffset) { + part.videoMetadata.startOffset = file.videoMetadata.startOffset; + } + if (file.videoMetadata.endOffset) { + part.videoMetadata.endOffset = file.videoMetadata.endOffset; + } + if (file.videoMetadata.fps) { + part.videoMetadata.fps = file.videoMetadata.fps; + } + } + + // Inject Per-Part Media Resolution (Gemini 3 feature) + // Only apply to supported media types (images, videos, pdfs) not text/code + // Prioritize file-level resolution, then global resolution + const effectiveResolution = file.mediaResolution || mediaResolution; + + if (part && isGemini3 && effectiveResolution && effectiveResolution !== MediaResolution.MEDIA_RESOLUTION_UNSPECIFIED) { + // Logic update: + // 1. If it's fileData (File API), we always inject resolution (unless it's YouTube link which uses fileUri but is special). + // 2. If it's inlineData, we ensure it's not text-like. + const shouldInject = (part.fileData && !isYoutube) || (part.inlineData && !isTextLike); + + if (shouldInject) { + part.mediaResolution = { level: effectiveResolution }; + } + } + + return { file: newFile, part }; + })); + + const enrichedFiles = processedResults.map(r => r.file); + const dataParts = processedResults.map(r => r.part).filter((p): p is ContentPart => p !== null); + + const userTypedText = text.trim(); + const contentPartsResult: ContentPart[] = []; + + // Optimize: Place media parts first as recommended by Gemini documentation for better multimodal performance + contentPartsResult.push(...dataParts); + + if (userTypedText) { + contentPartsResult.push({ text: userTypedText }); + } + + return { contentParts: contentPartsResult, enrichedFiles }; +}; + +export const createChatHistoryForApi = async ( + msgs: ChatMessage[], + stripThinking: boolean = false +): Promise => { + const historyItemsPromises = msgs + .filter(msg => (msg.role === 'user' || msg.role === 'model') && !msg.excludeFromContext) + .map(async (msg) => { + let contentToUse = msg.content; + + if (stripThinking) { + // Remove blocks including tags from the content + // Matches ... to handle variations and potential hallucinated closing tags + contentToUse = contentToUse.replace(/[\s\S]*?<\/[^>]+>/gi, '').trim(); + } + + // Use buildContentParts for both user and model messages to handle text and files consistently. + const { contentParts } = await buildContentParts(contentToUse, msg.files); + + // Attach Thought Signatures if present (Crucial for Gemini 3 Pro) + if (msg.role === 'model' && msg.thoughtSignatures && msg.thoughtSignatures.length > 0) { + if (contentParts.length > 0) { + const lastPart = contentParts[contentParts.length - 1]; + lastPart.thoughtSignature = msg.thoughtSignatures[msg.thoughtSignatures.length - 1]; + } + } + + return { role: msg.role as 'user' | 'model', parts: contentParts }; + }); + + return Promise.all(historyItemsPromises); +}; \ No newline at end of file diff --git a/all-model-chat/utils/chat/ids.ts b/all-model-chat/apps/web/utils/chat/ids.ts similarity index 100% rename from all-model-chat/utils/chat/ids.ts rename to all-model-chat/apps/web/utils/chat/ids.ts diff --git a/all-model-chat/utils/chat/parsing.ts b/all-model-chat/apps/web/utils/chat/parsing.ts similarity index 100% rename from all-model-chat/utils/chat/parsing.ts rename to all-model-chat/apps/web/utils/chat/parsing.ts diff --git a/all-model-chat/utils/chat/session.ts b/all-model-chat/apps/web/utils/chat/session.ts similarity index 98% rename from all-model-chat/utils/chat/session.ts rename to all-model-chat/apps/web/utils/chat/session.ts index d7899390..8a77cc92 100644 --- a/all-model-chat/utils/chat/session.ts +++ b/all-model-chat/apps/web/utils/chat/session.ts @@ -6,6 +6,7 @@ import { SUPPORTED_IMAGE_MIME_TYPES } from '../../constants/fileConstants'; import { logService } from '../../services/logService'; import { DEFAULT_CHAT_SETTINGS } from '../../constants/appConstants'; import { base64ToBlob } from '../fileHelpers'; +import { sanitizeChatSettingsForStorage } from '../security/sensitiveData'; export const createMessage = ( role: 'user' | 'model' | 'error', @@ -108,6 +109,7 @@ export const rehydrateSessionFiles = (session: SavedChatSession): SavedChatSessi export const sanitizeSessionForExport = (session: SavedChatSession): SavedChatSession => { return { ...session, + settings: sanitizeChatSettingsForStorage(session.settings), messages: session.messages.map(msg => { if (!msg.files) return msg; return { @@ -238,4 +240,4 @@ export const updateSessionWithNewMessages = ( } return s; }); -}; \ No newline at end of file +}; diff --git a/all-model-chat/utils/chatHelpers.ts b/all-model-chat/apps/web/utils/chatHelpers.ts similarity index 100% rename from all-model-chat/utils/chatHelpers.ts rename to all-model-chat/apps/web/utils/chatHelpers.ts diff --git a/all-model-chat/utils/clipboardUtils.ts b/all-model-chat/apps/web/utils/clipboardUtils.ts similarity index 100% rename from all-model-chat/utils/clipboardUtils.ts rename to all-model-chat/apps/web/utils/clipboardUtils.ts diff --git a/all-model-chat/utils/codeUtils.ts b/all-model-chat/apps/web/utils/codeUtils.ts similarity index 100% rename from all-model-chat/utils/codeUtils.ts rename to all-model-chat/apps/web/utils/codeUtils.ts diff --git a/all-model-chat/utils/dateHelpers.ts b/all-model-chat/apps/web/utils/dateHelpers.ts similarity index 100% rename from all-model-chat/utils/dateHelpers.ts rename to all-model-chat/apps/web/utils/dateHelpers.ts diff --git a/all-model-chat/utils/db.ts b/all-model-chat/apps/web/utils/db.ts similarity index 100% rename from all-model-chat/utils/db.ts rename to all-model-chat/apps/web/utils/db.ts diff --git a/all-model-chat/utils/domainUtils.ts b/all-model-chat/apps/web/utils/domainUtils.ts similarity index 100% rename from all-model-chat/utils/domainUtils.ts rename to all-model-chat/apps/web/utils/domainUtils.ts diff --git a/all-model-chat/utils/export/core.ts b/all-model-chat/apps/web/utils/export/core.ts similarity index 100% rename from all-model-chat/utils/export/core.ts rename to all-model-chat/apps/web/utils/export/core.ts diff --git a/all-model-chat/utils/export/dom.ts b/all-model-chat/apps/web/utils/export/dom.ts similarity index 100% rename from all-model-chat/utils/export/dom.ts rename to all-model-chat/apps/web/utils/export/dom.ts diff --git a/all-model-chat/utils/export/files.ts b/all-model-chat/apps/web/utils/export/files.ts similarity index 100% rename from all-model-chat/utils/export/files.ts rename to all-model-chat/apps/web/utils/export/files.ts diff --git a/all-model-chat/utils/export/image.ts b/all-model-chat/apps/web/utils/export/image.ts similarity index 100% rename from all-model-chat/utils/export/image.ts rename to all-model-chat/apps/web/utils/export/image.ts diff --git a/all-model-chat/utils/export/templates.ts b/all-model-chat/apps/web/utils/export/templates.ts similarity index 100% rename from all-model-chat/utils/export/templates.ts rename to all-model-chat/apps/web/utils/export/templates.ts diff --git a/all-model-chat/utils/exportUtils.ts b/all-model-chat/apps/web/utils/exportUtils.ts similarity index 100% rename from all-model-chat/utils/exportUtils.ts rename to all-model-chat/apps/web/utils/exportUtils.ts diff --git a/all-model-chat/utils/fileHelpers.ts b/all-model-chat/apps/web/utils/fileHelpers.ts similarity index 100% rename from all-model-chat/utils/fileHelpers.ts rename to all-model-chat/apps/web/utils/fileHelpers.ts diff --git a/all-model-chat/apps/web/utils/folderImportUtils.ts b/all-model-chat/apps/web/utils/folderImportUtils.ts new file mode 100644 index 00000000..ff7cb6d0 --- /dev/null +++ b/all-model-chat/apps/web/utils/folderImportUtils.ts @@ -0,0 +1,412 @@ + +import { fileToString } from './domainUtils'; +import JSZip from 'jszip'; +import { ProjectContext, ProjectContextFile, FileTreeNode } from '../types/chat'; + +const IGNORED_EXTENSIONS = new Set([ + '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.svg', '.ico', '.webp', + '.mp3', '.wav', '.ogg', '.mp4', '.mov', '.avi', '.webm', + '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', + '.zip', '.rar', '.7z', '.tar', '.gz', + '.exe', '.dll', '.so', '.o', '.a', '.obj', + '.class', '.jar', '.pyc', '.pyd', + '.ds_store', + '.eot', '.ttf', '.woff', '.woff2', +]); + +const IGNORED_DIRS = new Set(['.git', 'node_modules', '__pycache__', '.vscode', '.idea', 'dist', 'build', 'out', 'target', 'coverage', '.next', '.nuxt']); + +// Internal node type for legacy tree building (kept for backward compat) +interface FileNode { + name: string; + children: FileNode[]; + isDirectory: boolean; +} + +interface ProcessingEntry { + path: string; + // Helper to retrieve content only if the file passes filtering + getContent: () => Promise; + size?: number; +} + +// Entry type for agentic mode (stores File ref instead of content getter) +interface AgenticEntry { + path: string; + file: File; + size: number; +} + +// Check if a path should be ignored based on directory or extension +const shouldIgnore = (path: string): boolean => { + const parts = path.split('/').filter(p => p); + if (parts.some(part => IGNORED_DIRS.has(part) || part.startsWith('.'))) return true; + + const extension = `.${path.split('.').pop()?.toLowerCase()}`; + if (IGNORED_EXTENSIONS.has(extension)) return true; + + return false; +}; + +function buildASCIITree(treeData: FileNode[], rootName: string = 'root'): string { + let structure = `${rootName}\n`; + const generateLines = (nodes: FileNode[], prefix: string) => { + // Sort: directories first, then files, alphabetically + nodes.sort((a, b) => { + if (a.isDirectory === b.isDirectory) return a.name.localeCompare(b.name); + return a.isDirectory ? -1 : 1; + }); + + nodes.forEach((node, index) => { + const isLast = index === nodes.length - 1; + const connector = isLast ? '└── ' : '├── '; + structure += `${prefix}${connector}${node.name}\n`; + if (node.isDirectory && node.children.length > 0) { + const newPrefix = prefix + (isLast ? ' ' : '│ '); + generateLines(node.children, newPrefix); + } + }); + }; + generateLines(treeData, ''); + return structure; +} + +/** + * Unified logic to iterate entries, build the tree structure, and read content. + */ +const processFileEntries = async (entries: ProcessingEntry[], rootName: string): Promise => { + const nodeMap = new Map(); + const roots: FileNode[] = []; + const processedFiles: { path: string, content: string }[] = []; + + // Max file size for text extraction (2MB) + const MAX_TEXT_SIZE = 2 * 1024 * 1024; + + for (const entry of entries) { + // 1. Filtering + if (shouldIgnore(entry.path)) continue; + if (entry.size !== undefined && entry.size > MAX_TEXT_SIZE) continue; + + // 2. Tree Building + const parts = entry.path.split('/').filter(p => p); + let parentNode: FileNode | undefined = undefined; + let currentPath = ''; + + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + currentPath = parts.slice(0, i + 1).join('/'); + + let currentNode = nodeMap.get(currentPath); + + if (!currentNode) { + const isDir = i < parts.length - 1; + currentNode = { + name: part, + children: [], + isDirectory: isDir, + }; + nodeMap.set(currentPath, currentNode); + + if (parentNode) { + parentNode.children.push(currentNode); + } else { + roots.push(currentNode); + } + } + parentNode = currentNode; + } + + // 3. Content Reading (Only for files, i.e., leaf nodes in this iteration context) + try { + const content = await entry.getContent(); + processedFiles.push({ path: entry.path, content }); + } catch (e) { + console.warn(`Failed to read content for ${entry.path}`, e); + } + } + + // 4. Output Generation + // Optimization: If single root folder, promote its name to rootName to avoid "Project/ProjectName/..." redundancy + let effectiveRootName = rootName; + let effectiveRoots = roots; + + if (roots.length === 1 && roots[0].isDirectory) { + effectiveRootName = roots[0].name; + effectiveRoots = roots[0].children; // Start tree from inside the folder + } + + const structureString = buildASCIITree(effectiveRoots, effectiveRootName); + + let output = "File Structure:\n"; + output += structureString; + output += "\n\nFile Contents:\n"; + + // Sort files by path for consistent output + processedFiles.sort((a, b) => a.path.localeCompare(b.path)); + + for (const file of processedFiles) { + output += `\n--- START OF FILE ${file.path} ---\n`; + output += file.content; + if (file.content && !file.content.endsWith('\n')) { + output += '\n'; + } + output += `--- END OF FILE ${file.path} ---\n`; + } + + const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); + return new File([output], `${effectiveRootName}-context-${timestamp}.txt`, { type: 'text/plain' }); +}; + +export const generateFolderContext = async (files: FileList | File[] | { file: File, path: string }[]): Promise => { + const items = Array.isArray(files) ? files : Array.from(files); + + const entries: ProcessingEntry[] = items.map(item => { + let file: File; + let path: string; + + if ('file' in item && 'path' in item && typeof item.path === 'string') { + // Custom object from Drag & Drop hook + file = item.file; + path = item.path; + } else { + // Standard File object + file = item as File; + path = (file as any).webkitRelativePath || file.name; + } + + return { + path, + size: file.size, + getContent: () => fileToString(file) + }; + }); + + return processFileEntries(entries, "Project"); +}; + +export const generateZipContext = async (zipFile: File): Promise => { + const zip = await JSZip.loadAsync(zipFile); + const zipObjects = Object.values(zip.files) as JSZip.JSZipObject[]; + + const entries: ProcessingEntry[] = zipObjects + .filter(obj => !obj.dir) // Filter out directory entries + .map(obj => ({ + path: obj.name, + getContent: async () => { + // Read as Uint8Array first to detect binary content + const data = await obj.async('uint8array'); + + // Heuristic: Check for null bytes (0x00) in the first 8KB to detect binary files + const checkLimit = Math.min(data.length, 8000); + for (let i = 0; i < checkLimit; i++) { + if (data[i] === 0x00) { + return `[Binary content skipped for file: ${obj.name}]`; + } + } + + // If safe, decode as UTF-8 text + return new TextDecoder().decode(data); + } + })); + + let rootName = zipFile.name.replace(/\.zip$/i, ''); + return processFileEntries(entries, rootName); +}; + +/** + * Build a ProjectContext from a ZIP file (agentic mode). + * This avoids converting content into a single text file and instead + * creates file references for on-demand reading via read_file. + */ +export const buildProjectContextFromZip = async (zipFile: File): Promise => { + const zip = await JSZip.loadAsync(zipFile); + const zipObjects = Object.values(zip.files).filter(obj => !obj.dir); + + const entries: { file: File, path: string }[] = []; + + for (const obj of zipObjects) { + const blob = await obj.async('blob'); + const filename = obj.name.split('/').pop() || obj.name; + const file = new File([blob], filename, { type: blob.type || '' }); + entries.push({ file, path: obj.name }); + } + + const context = buildProjectContext(entries); + + // If no explicit root folder exists, use zip filename as root + if (context.rootName === 'Project') { + const zipRootName = zipFile.name.replace(/\.zip$/i, '') || 'Project'; + if (zipRootName !== context.rootName) { + context.rootName = zipRootName; + const lines = context.fileTree.split('\n'); + context.fileTree = lines.length > 1 + ? `${zipRootName}\n${lines.slice(1).join('\n')}` + : zipRootName; + } + } + + return context; +}; + +// ============ Agentic Folder Access Functions ============ + +/** + * Build a ProjectContext for agentic file access. + * Unlike generateFolderContext, this does NOT read file contents. + * It only builds the tree structure and stores file references for on-demand reading. + */ +export const buildProjectContext = (files: { file: File, path: string }[]): ProjectContext => { + const fileMap = new Map(); + const nodeMap = new Map(); + const roots: FileTreeNode[] = []; + + let totalSize = 0; + let totalFiles = 0; + + // Max file size for text extraction (2MB) + const MAX_TEXT_SIZE = 2 * 1024 * 1024; + + for (const { file, path } of files) { + // Apply same filtering as legacy mode + if (shouldIgnore(path)) continue; + if (file.size > MAX_TEXT_SIZE) continue; + + totalSize += file.size; + totalFiles++; + + // Store file reference for later reading + fileMap.set(path, { + path, + size: file.size, + fileRef: file, + }); + + // Build tree structure + const parts = path.split('/').filter(p => p); + let currentPath = ''; + let parentNode: FileTreeNode | undefined = undefined; + + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + currentPath = parts.slice(0, i + 1).join('/'); + + let currentNode = nodeMap.get(currentPath); + + if (!currentNode) { + const isDir = i < parts.length - 1; + currentNode = { + name: part, + path: currentPath, + isDirectory: isDir, + children: isDir ? [] : undefined, + size: isDir ? undefined : file.size, + }; + nodeMap.set(currentPath, currentNode); + + if (parentNode && parentNode.children) { + parentNode.children.push(currentNode); + } else if (!parentNode) { + roots.push(currentNode); + } + } + parentNode = currentNode; + } + } + + // Determine effective root + let effectiveRootName = 'Project'; + let effectiveRoots = roots; + + if (roots.length === 1 && roots[0].isDirectory) { + effectiveRootName = roots[0].name; + effectiveRoots = roots[0].children || []; + } + + // Sort tree nodes (directories first, then alphabetically) + const sortNodes = (nodes: FileTreeNode[]) => { + nodes.sort((a, b) => { + if (a.isDirectory === b.isDirectory) return a.name.localeCompare(b.name); + return a.isDirectory ? -1 : 1; + }); + nodes.forEach(node => { + if (node.children) sortNodes(node.children); + }); + }; + sortNodes(effectiveRoots); + + // Build ASCII tree for system prompt injection + const buildTree = (nodes: FileTreeNode[], prefix: string): string => { + let result = ''; + nodes.forEach((node, index) => { + const isLast = index === nodes.length - 1; + const connector = isLast ? '└── ' : '├── '; + result += `${prefix}${connector}${node.name}\n`; + if (node.isDirectory && node.children && node.children.length > 0) { + const newPrefix = prefix + (isLast ? ' ' : '│ '); + result += buildTree(node.children, newPrefix); + } + }); + return result; + }; + + const fileTree = `${effectiveRootName}\n${buildTree(effectiveRoots, '')}`; + + return { + rootName: effectiveRootName, + fileTree, + treeNodes: effectiveRoots, + fileMap, + totalFiles, + totalSize, + isPersistent: false, + createdAt: new Date(), + }; +}; + +/** + * Read a single file from a ProjectContext. + * This is the function called when AI uses the read_file tool. + * Handles path normalization to match fileMap keys. + */ +export const readProjectFile = async (context: ProjectContext, filepath: string): Promise => { + // Normalize: remove leading/trailing slashes and './' + let normalizedPath = filepath.replace(/^\.?\/*/, '').replace(/\/*$/, ''); + + // Try exact match first + let fileEntry = context.fileMap.get(normalizedPath); + + // If not found and path starts with rootName, try without it + if (!fileEntry && normalizedPath.startsWith(context.rootName + '/')) { + const withoutRoot = normalizedPath.slice(context.rootName.length + 1); + fileEntry = context.fileMap.get(withoutRoot); + } + + // If still not found, try adding rootName prefix (reverse case) + if (!fileEntry) { + const withRoot = `${context.rootName}/${normalizedPath}`; + fileEntry = context.fileMap.get(withRoot); + } + + // If still not found, try partial path matching (last segments) + if (!fileEntry) { + const pathParts = normalizedPath.split('/'); + for (const [key, value] of context.fileMap.entries()) { + if (key.endsWith(normalizedPath) || key.endsWith('/' + pathParts.slice(-2).join('/'))) { + fileEntry = value; + break; + } + } + } + + if (!fileEntry) { + // Provide helpful error with available paths + const availablePaths = Array.from(context.fileMap.keys()).slice(0, 10); + throw new Error(`File not found: ${filepath}. Available paths include: ${availablePaths.join(', ')}`); + } + + try { + return await fileToString(fileEntry.fileRef); + } catch (e) { + throw new Error(`Failed to read file: ${filepath}`); + } +}; diff --git a/all-model-chat/utils/htmlToMarkdown.ts b/all-model-chat/apps/web/utils/htmlToMarkdown.ts similarity index 100% rename from all-model-chat/utils/htmlToMarkdown.ts rename to all-model-chat/apps/web/utils/htmlToMarkdown.ts diff --git a/all-model-chat/utils/markdownConfig.ts b/all-model-chat/apps/web/utils/markdownConfig.ts similarity index 100% rename from all-model-chat/utils/markdownConfig.ts rename to all-model-chat/apps/web/utils/markdownConfig.ts diff --git a/all-model-chat/utils/mediaUtils.ts b/all-model-chat/apps/web/utils/mediaUtils.ts similarity index 100% rename from all-model-chat/utils/mediaUtils.ts rename to all-model-chat/apps/web/utils/mediaUtils.ts diff --git a/all-model-chat/utils/modelHelpers.ts b/all-model-chat/apps/web/utils/modelHelpers.ts similarity index 100% rename from all-model-chat/utils/modelHelpers.ts rename to all-model-chat/apps/web/utils/modelHelpers.ts diff --git a/all-model-chat/apps/web/utils/security/legacy_cleanup.ts b/all-model-chat/apps/web/utils/security/legacy_cleanup.ts new file mode 100644 index 00000000..d8c48b1a --- /dev/null +++ b/all-model-chat/apps/web/utils/security/legacy_cleanup.ts @@ -0,0 +1,69 @@ +import { API_KEY_LAST_USED_INDEX_KEY } from '../../constants/appConstants'; +import { AppSettings, SavedChatSession } from '../../types'; +import { dbService } from '../db'; +import { sanitizeAppSettingsForStorage, sanitizeSessionForStorage } from './sensitiveData'; + +const LEGACY_API_USAGE_STORAGE_KEY = 'chatApiUsageData'; +const MIGRATION_VERSION_STORAGE_KEY = 'allModelChat.securityMigration.version'; +const SENSITIVE_DATA_MIGRATION_VERSION = 1; + +const hasAppSensitiveValues = (settings: AppSettings): boolean => + (typeof settings.apiKey === 'string' && settings.apiKey.trim().length > 0) || + (typeof settings.lockedApiKey === 'string' && settings.lockedApiKey.trim().length > 0); + +const hasSessionSensitiveValues = (session: SavedChatSession): boolean => + typeof session.settings.lockedApiKey === 'string' && session.settings.lockedApiKey.trim().length > 0; + +const getStoredMigrationVersion = (): number => { + try { + const raw = localStorage.getItem(MIGRATION_VERSION_STORAGE_KEY); + if (!raw) return 0; + const parsed = parseInt(raw, 10); + return Number.isFinite(parsed) ? parsed : 0; + } catch { + return 0; + } +}; + +const setStoredMigrationVersion = (version: number): void => { + try { + localStorage.setItem(MIGRATION_VERSION_STORAGE_KEY, String(version)); + } catch { + // Ignore storage write failures in migration bookkeeping. + } +}; + +const clearLegacySensitiveLocalStorage = (): void => { + try { + localStorage.removeItem(LEGACY_API_USAGE_STORAGE_KEY); + localStorage.removeItem(API_KEY_LAST_USED_INDEX_KEY); + } catch { + // Ignore storage clear failures. + } +}; + +export const runSensitiveDataCleanupMigration = async (): Promise => { + const version = getStoredMigrationVersion(); + if (version >= SENSITIVE_DATA_MIGRATION_VERSION) { + return; + } + + clearLegacySensitiveLocalStorage(); + + try { + const storedSettings = await dbService.getAppSettings(); + if (storedSettings && hasAppSensitiveValues(storedSettings)) { + await dbService.setAppSettings(sanitizeAppSettingsForStorage(storedSettings)); + } + + const sessions = await dbService.getAllSessions(); + if (sessions.some(hasSessionSensitiveValues)) { + await dbService.setAllSessions(sessions.map(sanitizeSessionForStorage)); + } + } catch (error) { + console.error('Failed to run sensitive-data cleanup migration:', error); + return; + } + + setStoredMigrationVersion(SENSITIVE_DATA_MIGRATION_VERSION); +}; diff --git a/all-model-chat/apps/web/utils/security/sensitiveData.ts b/all-model-chat/apps/web/utils/security/sensitiveData.ts new file mode 100644 index 00000000..d8f396c3 --- /dev/null +++ b/all-model-chat/apps/web/utils/security/sensitiveData.ts @@ -0,0 +1,26 @@ +import { AppSettings, ChatSettings, SavedChatSession } from '../../types'; + +/** + * Keep session-level settings free of raw provider secrets before persistence/export. + */ +export const sanitizeChatSettingsForStorage = (settings: ChatSettings): ChatSettings => ({ + ...settings, + lockedApiKey: null, +}); + +/** + * Keep app-level settings free of raw provider secrets before persistence/export. + */ +export const sanitizeAppSettingsForStorage = (settings: AppSettings): AppSettings => ({ + ...settings, + apiKey: null, + lockedApiKey: null, +}); + +/** + * Keep stored/exported session payloads free of raw provider secrets. + */ +export const sanitizeSessionForStorage = (session: SavedChatSession): SavedChatSession => ({ + ...session, + settings: sanitizeChatSettingsForStorage(session.settings), +}); diff --git a/all-model-chat/utils/shortcutUtils.ts b/all-model-chat/apps/web/utils/shortcutUtils.ts similarity index 100% rename from all-model-chat/utils/shortcutUtils.ts rename to all-model-chat/apps/web/utils/shortcutUtils.ts diff --git a/all-model-chat/utils/translations.ts b/all-model-chat/apps/web/utils/translations.ts similarity index 100% rename from all-model-chat/utils/translations.ts rename to all-model-chat/apps/web/utils/translations.ts diff --git a/all-model-chat/utils/translations/app.ts b/all-model-chat/apps/web/utils/translations/app.ts similarity index 100% rename from all-model-chat/utils/translations/app.ts rename to all-model-chat/apps/web/utils/translations/app.ts diff --git a/all-model-chat/utils/translations/chatInput.ts b/all-model-chat/apps/web/utils/translations/chatInput.ts similarity index 97% rename from all-model-chat/utils/translations/chatInput.ts rename to all-model-chat/apps/web/utils/translations/chatInput.ts index 5b70f0cf..686df4de 100644 --- a/all-model-chat/utils/translations/chatInput.ts +++ b/all-model-chat/apps/web/utils/translations/chatInput.ts @@ -16,8 +16,8 @@ export const chatInputTranslations = { attachMenu_title: { en: 'Attach file', zh: '附加文件' }, attachMenu_aria: { en: 'Attach file menu', zh: '附加文件菜单' }, attachMenu_upload: { en: 'Upload from Device', zh: '从设备上传' }, - attachMenu_importFolder: { en: 'Import Folder (as Text)', zh: '导入文件夹 (转为文本)' }, - attachMenu_importZip: { en: 'Import Zip (as Text)', zh: '导入 Zip (转为文本)' }, + attachMenu_importFolder: { en: 'Import Folder', zh: '导入文件夹' }, + attachMenu_importZip: { en: 'Import Zip', zh: '导入压缩包' }, attachMenu_gallery: { en: 'Gallery', zh: '图库' }, attachMenu_takePhoto: { en: 'Take Photo', zh: '拍照' }, attachMenu_screenshot: { en: 'Screenshot', zh: '屏幕截图' }, diff --git a/all-model-chat/utils/translations/common.ts b/all-model-chat/apps/web/utils/translations/common.ts similarity index 100% rename from all-model-chat/utils/translations/common.ts rename to all-model-chat/apps/web/utils/translations/common.ts diff --git a/all-model-chat/utils/translations/header.ts b/all-model-chat/apps/web/utils/translations/header.ts similarity index 100% rename from all-model-chat/utils/translations/header.ts rename to all-model-chat/apps/web/utils/translations/header.ts diff --git a/all-model-chat/utils/translations/history.ts b/all-model-chat/apps/web/utils/translations/history.ts similarity index 100% rename from all-model-chat/utils/translations/history.ts rename to all-model-chat/apps/web/utils/translations/history.ts diff --git a/all-model-chat/utils/translations/messages.ts b/all-model-chat/apps/web/utils/translations/messages.ts similarity index 100% rename from all-model-chat/utils/translations/messages.ts rename to all-model-chat/apps/web/utils/translations/messages.ts diff --git a/all-model-chat/utils/translations/scenarios.ts b/all-model-chat/apps/web/utils/translations/scenarios.ts similarity index 100% rename from all-model-chat/utils/translations/scenarios.ts rename to all-model-chat/apps/web/utils/translations/scenarios.ts diff --git a/all-model-chat/utils/translations/settings.ts b/all-model-chat/apps/web/utils/translations/settings.ts similarity index 100% rename from all-model-chat/utils/translations/settings.ts rename to all-model-chat/apps/web/utils/translations/settings.ts diff --git a/all-model-chat/utils/translations/settings/about.ts b/all-model-chat/apps/web/utils/translations/settings/about.ts similarity index 100% rename from all-model-chat/utils/translations/settings/about.ts rename to all-model-chat/apps/web/utils/translations/settings/about.ts diff --git a/all-model-chat/utils/translations/settings/api.ts b/all-model-chat/apps/web/utils/translations/settings/api.ts similarity index 100% rename from all-model-chat/utils/translations/settings/api.ts rename to all-model-chat/apps/web/utils/translations/settings/api.ts diff --git a/all-model-chat/utils/translations/settings/appearance.ts b/all-model-chat/apps/web/utils/translations/settings/appearance.ts similarity index 100% rename from all-model-chat/utils/translations/settings/appearance.ts rename to all-model-chat/apps/web/utils/translations/settings/appearance.ts diff --git a/all-model-chat/utils/translations/settings/data.ts b/all-model-chat/apps/web/utils/translations/settings/data.ts similarity index 90% rename from all-model-chat/utils/translations/settings/data.ts rename to all-model-chat/apps/web/utils/translations/settings/data.ts index 29ca810d..20aa8145 100644 --- a/all-model-chat/utils/translations/settings/data.ts +++ b/all-model-chat/apps/web/utils/translations/settings/data.ts @@ -7,7 +7,7 @@ export const dataSettings = { settingsClearHistory_confirm: { en: 'Are you sure you want to delete all chat history and groups? This action cannot be undone.', zh: '您确定要删除所有聊天记录和分组吗?此操作无法撤销。' }, settingsClearCache: { en: 'Clear All App Data', zh: '清除所有应用数据' }, settingsClearCache_aria: { en: 'Clear all application settings and data', zh: '清除所有应用设置和数据' }, - settingsClearCache_confirm: { en: "Are you sure you want to clear all application data?\n\nThis will remove:\n- Saved settings\n- Chat history\n- Preloaded scenarios\n\nThis action cannot be undone.", zh: '您确定要清除所有应用数据吗?\n\n这将删除:\n- 已保存的设置\n- 聊天记录\n- 预加载的场景\n\n此操作无法撤销。'}, + settingsClearCache_confirm: { en: "Are you sure you want to clear all application data?\n\nThis will remove:\n- Saved settings\n- Chat history\n- Preloaded scenarios\n\nThis action cannot be undone.", zh: '您确定要清除所有应用数据吗?\n\n这将删除:\n- 已保存的设置\n- 聊天记录\n- 预加载的场景\n\n此操作无法撤销。' }, settingsInstallApp: { en: 'Install App', zh: '安装应用' }, settingsInstallApp_aria: { en: 'Install Progressive Web App', zh: '安装渐进式网络应用' }, settingsInstallApp_available_title: { en: 'Install Progressive Web App', zh: '安装渐进式网络应用' }, @@ -31,4 +31,8 @@ export const dataSettings = { settingsViewLogs_title: { en: 'Open Application Logs (Ctrl+Alt+L)', zh: '查看应用日志 (Ctrl+Alt+L)' }, settingsClearLogs: { en: 'Clear Logs', zh: '清空日志' }, settingsClearLogs_confirm: { en: 'Are you sure you want to clear all system logs?', zh: '您确定要清空所有系统日志吗?' }, + settingsUnsavedChanges: { en: 'Unsaved Changes', zh: '未保存的更改' }, + settingsUnsavedChanges_confirm: { en: 'You have unsaved changes. Do you want to save them before closing?', zh: '您有未保存的更改。关闭前是否要保存?' }, + settingsSaveChanges: { en: 'Save Changes', zh: '保存更改' }, + settingsDiscardChanges: { en: 'Discard', zh: '放弃更改' }, }; \ No newline at end of file diff --git a/all-model-chat/utils/translations/settings/general.ts b/all-model-chat/apps/web/utils/translations/settings/general.ts similarity index 100% rename from all-model-chat/utils/translations/settings/general.ts rename to all-model-chat/apps/web/utils/translations/settings/general.ts diff --git a/all-model-chat/utils/translations/settings/model.ts b/all-model-chat/apps/web/utils/translations/settings/model.ts similarity index 100% rename from all-model-chat/utils/translations/settings/model.ts rename to all-model-chat/apps/web/utils/translations/settings/model.ts diff --git a/all-model-chat/utils/translations/settings/safety.ts b/all-model-chat/apps/web/utils/translations/settings/safety.ts similarity index 100% rename from all-model-chat/utils/translations/settings/safety.ts rename to all-model-chat/apps/web/utils/translations/settings/safety.ts diff --git a/all-model-chat/utils/translations/settings/shortcuts.ts b/all-model-chat/apps/web/utils/translations/settings/shortcuts.ts similarity index 100% rename from all-model-chat/utils/translations/settings/shortcuts.ts rename to all-model-chat/apps/web/utils/translations/settings/shortcuts.ts diff --git a/all-model-chat/utils/uiUtils.ts b/all-model-chat/apps/web/utils/uiUtils.ts similarity index 100% rename from all-model-chat/utils/uiUtils.ts rename to all-model-chat/apps/web/utils/uiUtils.ts diff --git a/all-model-chat/apps/web/vite.config.ts b/all-model-chat/apps/web/vite.config.ts new file mode 100644 index 00000000..fa542540 --- /dev/null +++ b/all-model-chat/apps/web/vite.config.ts @@ -0,0 +1,51 @@ + +import path from 'path'; +import { defineConfig, loadEnv } from 'vite'; +import react from '@vitejs/plugin-react'; +import tailwindcss from 'tailwindcss'; +import autoprefixer from 'autoprefixer'; + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, '.', ''); + const bffProxyTarget = env.VITE_BFF_PROXY_TARGET || 'http://127.0.0.1:8787'; + + return { + plugins: [react()], + css: { + postcss: { + plugins: [ + tailwindcss({ + content: [ + "./index.html", + "./{components,hooks,services,utils,constants,contexts,types,styles}/**/*.{js,ts,jsx,tsx}", + "./*.{js,ts,jsx,tsx}" + ], + theme: { + extend: {}, + }, + plugins: [], + }), + autoprefixer(), + ], + }, + }, + define: { + 'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY) + }, + server: { + proxy: { + '/api': { + target: bffProxyTarget, + changeOrigin: true, + }, + }, + }, + resolve: { + alias: { + // __dirname is not available in ES modules. + // We'll resolve from the current working directory. + '@': path.resolve('.'), + } + } + }; +}); diff --git a/all-model-chat/components/log-viewer/ApiUsageTab.tsx b/all-model-chat/components/log-viewer/ApiUsageTab.tsx deleted file mode 100644 index 41e4462d..00000000 --- a/all-model-chat/components/log-viewer/ApiUsageTab.tsx +++ /dev/null @@ -1,66 +0,0 @@ - -import React from 'react'; -import { KeyRound, CheckCircle } from 'lucide-react'; -import { AppSettings, ChatSettings } from '../../types'; -import { ObfuscatedApiKey } from './ObfuscatedApiKey'; -import { parseApiKeys } from '../../utils/apiUtils'; - -interface ApiUsageTabProps { - apiKeyUsage: Map; - appSettings: AppSettings; - currentChatSettings: ChatSettings; -} - -export const ApiUsageTab: React.FC = ({ apiKeyUsage, appSettings, currentChatSettings }) => { - // Sanitize keys to match how they are logged in utils/apiUtils.ts (strip quotes, split by newlines/commas) - const allApiKeys = parseApiKeys(appSettings.apiKey); - - const displayApiKeyUsage = new Map(); - - // 1. Add keys from settings, checking usage logs - allApiKeys.forEach(key => displayApiKeyUsage.set(key, apiKeyUsage.get(key) || 0)); - - // 2. Add any keys found in usage logs that aren't currently in settings (historical keys) - apiKeyUsage.forEach((count, key) => { - if (!displayApiKeyUsage.has(key)) { - displayApiKeyUsage.set(key, count); - } - }); - - const totalApiUsage = Array.from(displayApiKeyUsage.values()).reduce((sum, count) => sum + count, 0); - - return ( -
-

API Key Usage Statistics

-
- {Array.from(displayApiKeyUsage.entries()) - .sort(([, a], [, b]) => b - a) - .map(([key, count], index) => { - const percentage = totalApiUsage > 0 ? (count / totalApiUsage) * 100 : 0; - const isActive = currentChatSettings.lockedApiKey === key; - return ( -
-
- #{index + 1} - {isActive && Active} -
-
- -
-
-
- {count} - requests -
-
- {percentage.toFixed(0)}% -
-
-
-
- ); - })} -
-
- ); -}; diff --git a/all-model-chat/components/message/blocks/MermaidBlock.tsx b/all-model-chat/components/message/blocks/MermaidBlock.tsx deleted file mode 100644 index 7877a38e..00000000 --- a/all-model-chat/components/message/blocks/MermaidBlock.tsx +++ /dev/null @@ -1,190 +0,0 @@ - -import React, { useEffect, useState, useRef } from 'react'; -import mermaid from 'mermaid'; -import { Loader2, AlertTriangle, Download, Maximize, Code, Copy, Check, Sidebar } from 'lucide-react'; -import { SideViewContent, UploadedFile } from '../../../types'; -import { exportSvgAsImage } from '../../../utils/exportUtils'; -import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'; -import { MESSAGE_BLOCK_BUTTON_CLASS } from '../../../constants/appConstants'; - -interface MermaidBlockProps { - code: string; - onImageClick: (file: UploadedFile) => void; - isLoading: boolean; - themeId: string; - onOpenSidePanel: (content: SideViewContent) => void; -} - -export const MermaidBlock: React.FC = ({ code, onImageClick, isLoading: isMessageLoading, themeId, onOpenSidePanel }) => { - const [svg, setSvg] = useState(''); - const [error, setError] = useState(''); - const [isRendering, setIsRendering] = useState(true); - const [isDownloading, setIsDownloading] = useState(false); - const [diagramFile, setDiagramFile] = useState(null); - const [showSource, setShowSource] = useState(false); - const { isCopied, copyToClipboard } = useCopyToClipboard(); - const diagramContainerRef = useRef(null); - - useEffect(() => { - let isMounted = true; - - // Debounce rendering to avoid syntax errors while typing - const timeoutId = setTimeout(async () => { - if (!code) return; - - try { - const id = `mermaid-svg-${Math.random().toString(36).substring(2, 9)}`; - - mermaid.initialize({ - startOnLoad: false, - theme: themeId === 'onyx' ? 'dark' : 'default', - securityLevel: 'loose', - fontFamily: 'inherit' - }); - - const { svg: renderedSvg } = await mermaid.render(id, code); - - if (!isMounted) return; - - setSvg(renderedSvg); - - const svgDataUrl = `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(renderedSvg)))}`; - setDiagramFile({ - id: id, - name: 'mermaid-diagram.svg', - type: 'image/svg+xml', - size: renderedSvg.length, - dataUrl: svgDataUrl, - uploadState: 'active' - }); - setError(''); - setIsRendering(false); - - } catch (e) { - if (!isMounted) return; - - if (isMessageLoading) { - // If still loading (streaming), treat parsing errors as "generating..." - // Show spinner to indicate incomplete state - setIsRendering(true); - } else { - // Final error state - const errorMessage = e instanceof Error ? e.message : 'Failed to render Mermaid diagram.'; - setError(errorMessage.replace(/.*error:\s*/, '')); - setSvg(''); - setIsRendering(false); - } - } - }, 500); // 500ms debounce - - return () => { - isMounted = false; - clearTimeout(timeoutId); - }; - }, [code, isMessageLoading, themeId]); - - const handleDownloadJpg = async () => { - if (!svg || isDownloading) return; - setIsDownloading(true); - try { - await exportSvgAsImage(svg, `mermaid-diagram-${Date.now()}.jpg`, 3, 'image/jpeg'); - } catch (e) { - const errorMessage = e instanceof Error ? e.message : 'Failed to export diagram as JPG.'; - setError(errorMessage); - } finally { - setIsDownloading(false); - } - }; - - const handleCopyCode = () => { - copyToClipboard(code); - }; - - const containerClasses = "p-4 border border-[var(--theme-border-secondary)] rounded-md shadow-inner overflow-auto custom-scrollbar flex items-center justify-center min-h-[150px] transition-colors duration-300"; - const bgClass = themeId === 'onyx' ? 'bg-[var(--theme-bg-secondary)]' : 'bg-white'; - - if (isRendering) { - return ( -
- -
- ); - } - - if (error) { - return ( -
-
-
- - Mermaid Error -
{error}
-
-
- {/* Always show code on error so user can debug */} -
-
{code}
-
-
- ); - } - - return ( -
-
- Mermaid -
- - - {diagramFile && ( - <> - - - - )} -
-
- -
diagramFile && onImageClick(diagramFile)} - dangerouslySetInnerHTML={{ __html: svg }} - /> - - {showSource && ( -
-
- -
-
-                  {code}
-              
-
- )} -
- ); -}; diff --git a/all-model-chat/components/settings/SettingsModal.tsx b/all-model-chat/components/settings/SettingsModal.tsx deleted file mode 100644 index 1e9c35cb..00000000 --- a/all-model-chat/components/settings/SettingsModal.tsx +++ /dev/null @@ -1,144 +0,0 @@ - -import React from 'react'; -import { AppSettings, ModelOption } from '../../types'; -import { Theme } from '../../constants/themeConstants'; -import { translations } from '../../utils/appUtils'; -import { Modal } from '../shared/Modal'; -import { ConfirmationModal } from '../modals/ConfirmationModal'; -import { useSettingsLogic } from '../../hooks/features/useSettingsLogic'; -import { SettingsSidebar } from './SettingsSidebar'; -import { SettingsContent } from './SettingsContent'; - -interface SettingsModalProps { - isOpen: boolean; - onClose: () => void; - currentSettings: AppSettings; - availableModels: ModelOption[]; - availableThemes: Theme[]; - onSave: (newSettings: AppSettings) => void; - onClearAllHistory: () => void; - onClearCache: () => void; - onOpenLogViewer: () => void; - onInstallPwa: () => void; - isInstallable: boolean; - onImportSettings: (file: File) => void; - onExportSettings: () => void; - onImportHistory: (file: File) => void; - onExportHistory: () => void; - onImportScenarios: (file: File) => void; - onExportScenarios: () => void; - t: (key: keyof typeof translations) => string; - setAvailableModels: (models: ModelOption[]) => void; -} - -export const SettingsModal: React.FC = ({ - isOpen, onClose, currentSettings, availableModels, availableThemes, - onSave, onClearAllHistory, onClearCache, onOpenLogViewer, - onInstallPwa, isInstallable, t, - onImportSettings, onExportSettings, - onImportHistory, onExportHistory, - onImportScenarios, onExportScenarios, - setAvailableModels -}) => { - - const { - activeTab, - setActiveTab, - confirmConfig, - closeConfirm, - scrollContainerRef, - handleContentScroll, - handleResetToDefaults, - handleClearLogs, - handleRequestClearHistory, - handleRequestClearCache, - handleRequestImportHistory, - updateSetting, - handleModelChange, - tabs - } = useSettingsLogic({ - isOpen, - onClose, - currentSettings, - onSave, - onClearAllHistory, - onClearCache, - onOpenLogViewer, - onImportHistory, - t - }); - - if (!isOpen) return null; - - return ( - <> - - - - {/* Content Area */} -
- {/* Desktop Header */} -
-

- {t(tabs.find(t => t.id === activeTab)?.labelKey as any)} -

-
- - {/* Scrollable Content */} -
- { onOpenLogViewer(); onClose(); }} - onClearLogs={handleClearLogs} - onReset={handleResetToDefaults} - onInstallPwa={onInstallPwa} - isInstallable={isInstallable} - onImportSettings={onImportSettings} - onExportSettings={onExportSettings} - onImportHistory={handleRequestImportHistory} - onExportHistory={onExportHistory} - onImportScenarios={onImportScenarios} - onExportScenarios={onExportScenarios} - t={t} - /> -
-
-
- - {confirmConfig.isOpen && ( - - )} - - ); -}; diff --git a/all-model-chat/components/settings/sections/ApiConfigSection.tsx b/all-model-chat/components/settings/sections/ApiConfigSection.tsx deleted file mode 100644 index d5e44b6b..00000000 --- a/all-model-chat/components/settings/sections/ApiConfigSection.tsx +++ /dev/null @@ -1,174 +0,0 @@ - -import React, { useState, useEffect } from 'react'; -import { KeyRound } from 'lucide-react'; -import { useResponsiveValue } from '../../../hooks/useDevice'; -import { getClient } from '../../../services/api/baseApi'; -import { parseApiKeys } from '../../../utils/apiUtils'; -import { ApiConfigToggle } from './api-config/ApiConfigToggle'; -import { ApiKeyInput } from './api-config/ApiKeyInput'; -import { ApiProxySettings } from './api-config/ApiProxySettings'; -import { ApiConnectionTester } from './api-config/ApiConnectionTester'; -import { ModelOption } from '../../../types'; - -interface ApiConfigSectionProps { - useCustomApiConfig: boolean; - setUseCustomApiConfig: (value: boolean) => void; - apiKey: string | null; - setApiKey: (value: string | null) => void; - apiProxyUrl: string | null; - setApiProxyUrl: (value: string | null) => void; - useApiProxy: boolean; - setUseApiProxy: (value: boolean) => void; - availableModels: ModelOption[]; - t: (key: string) => string; -} - -const CONNECTION_TEST_MODELS: ModelOption[] = [ - { id: 'gemini-3-flash-preview', name: 'Gemini 3 Flash Preview' }, - { id: 'gemini-3-pro-preview', name: 'Gemini 3 Pro Preview' }, - { id: 'gemini-2.5-flash-preview-09-2025', name: 'Gemini 2.5 Flash' }, - { id: 'gemini-2.5-flash-lite-preview-09-2025', name: 'Gemini 2.5 Flash Lite' }, - { id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro' }, - { id: 'gemma-3-27b-it', name: 'Gemma 3 27b IT' }, -]; - -export const ApiConfigSection: React.FC = ({ - useCustomApiConfig, - setUseCustomApiConfig, - apiKey, - setApiKey, - apiProxyUrl, - setApiProxyUrl, - useApiProxy, - setUseApiProxy, - availableModels, - t, -}) => { - // Test connection state - const [testStatus, setTestStatus] = useState<'idle' | 'testing' | 'success' | 'error'>('idle'); - const [testMessage, setTestMessage] = useState(null); - const [testModelId, setTestModelId] = useState('gemini-3-flash-preview'); - - // State to manage overflow visibility during transitions - const [allowOverflow, setAllowOverflow] = useState(useCustomApiConfig); - - const iconSize = useResponsiveValue(18, 20); - const hasEnvKey = !!process.env.API_KEY; - - useEffect(() => { - let timer: NodeJS.Timeout; - if (useCustomApiConfig) { - // Delay allowing overflow until transition matches duration (300ms) to prevent clipping artifacts during expansion - timer = setTimeout(() => setAllowOverflow(true), 300); - } else { - // Immediately hide overflow when collapsing to ensure clean animation - setAllowOverflow(false); - } - return () => clearTimeout(timer); - }, [useCustomApiConfig]); - - const handleTestConnection = async () => { - // Pick the key that would be used - let keyToTest = apiKey; - - // If custom config is OFF, or ON but no key provided, we might fall back to env key if available. - // But for explicit testing, if custom config is ON, we should test what's in the box. - if (!useCustomApiConfig && hasEnvKey) { - keyToTest = process.env.API_KEY || null; - } - - if (useCustomApiConfig && !keyToTest) { - setTestStatus('error'); - setTestMessage("No API Key provided to test."); - return; - } - - if (!keyToTest) { - setTestStatus('error'); - setTestMessage("No API Key available."); - return; - } - - // Handle multiple keys - pick first for test - const keys = parseApiKeys(keyToTest); - const firstKey = keys[0]; - - if (!firstKey) { - setTestStatus('error'); - setTestMessage("Invalid API Key format."); - return; - } - - const effectiveUrl = (useCustomApiConfig && useApiProxy && apiProxyUrl) ? apiProxyUrl : null; - - setTestStatus('testing'); - setTestMessage(null); - - try { - // Use the base API helper to get a client with sanitation logic - const ai = getClient(firstKey, effectiveUrl); - - const modelIdToUse = testModelId || 'gemini-3-flash-preview'; - - await ai.models.generateContent({ - model: modelIdToUse, - contents: 'Hello', - }); - - setTestStatus('success'); - } catch (error) { - setTestStatus('error'); - setTestMessage(error instanceof Error ? error.message : String(error)); - } - }; - - return ( -
-
-

- - {t('settingsApiConfig')} -

-
- -
- - - {/* Content - collapsible area */} -
-
- { setApiKey(val); setTestStatus('idle'); }} - t={t} - /> - - { setUseApiProxy(val); setTestStatus('idle'); }} - apiProxyUrl={apiProxyUrl} - setApiProxyUrl={(val) => { setApiProxyUrl(val); setTestStatus('idle'); }} - t={t} - /> - - -
-
-
-
- ); -}; diff --git a/all-model-chat/hooks/chat-input/handlers/useFileSelectionHandlers.ts b/all-model-chat/hooks/chat-input/handlers/useFileSelectionHandlers.ts deleted file mode 100644 index b52301c9..00000000 --- a/all-model-chat/hooks/chat-input/handlers/useFileSelectionHandlers.ts +++ /dev/null @@ -1,83 +0,0 @@ - -import { useCallback, Dispatch, SetStateAction } from 'react'; -import { UploadedFile } from '../../../types'; -import { generateUniqueId } from '../../../utils/appUtils'; -import { generateFolderContext } from '../../../utils/folderImportUtils'; - -interface UseFileSelectionHandlersProps { - onProcessFiles: (files: FileList | File[]) => Promise; - setSelectedFiles: Dispatch>; - setAppFileError: (error: string | null) => void; - setIsConverting: Dispatch>; - justInitiatedFileOpRef: React.MutableRefObject; - fileInputRef: React.RefObject; - imageInputRef: React.RefObject; - folderInputRef: React.RefObject; - zipInputRef: React.RefObject; -} - -export const useFileSelectionHandlers = ({ - onProcessFiles, - setSelectedFiles, - setAppFileError, - setIsConverting, - justInitiatedFileOpRef, - fileInputRef, - imageInputRef, - folderInputRef, - zipInputRef, -}: UseFileSelectionHandlersProps) => { - - const handleFileChange = useCallback(async (event: React.ChangeEvent) => { - if (event.target.files?.length) { - justInitiatedFileOpRef.current = true; - await onProcessFiles(event.target.files); - } - if (fileInputRef.current) fileInputRef.current.value = ""; - if (imageInputRef.current) imageInputRef.current.value = ""; - }, [onProcessFiles, justInitiatedFileOpRef, fileInputRef, imageInputRef]); - - const handleFolderChange = useCallback(async (event: React.ChangeEvent) => { - if (event.target.files?.length) { - const tempId = generateUniqueId(); - setIsConverting(true); - setSelectedFiles(prev => [...prev, { - id: tempId, - name: 'Processing folder...', - type: 'application/x-directory', - size: 0, - isProcessing: true, - uploadState: 'pending' - }]); - - try { - justInitiatedFileOpRef.current = true; - const contextFile = await generateFolderContext(event.target.files); - setSelectedFiles(prev => prev.filter(f => f.id !== tempId)); - await onProcessFiles([contextFile]); - } catch (e) { - console.error(e); - setAppFileError("Failed to process folder structure."); - setSelectedFiles(prev => prev.filter(f => f.id !== tempId)); - } finally { - setIsConverting(false); - } - } - if (folderInputRef.current) folderInputRef.current.value = ""; - }, [setIsConverting, setSelectedFiles, onProcessFiles, setAppFileError, justInitiatedFileOpRef, folderInputRef]); - - const handleZipChange = useCallback(async (event: React.ChangeEvent) => { - if (event.target.files?.length) { - justInitiatedFileOpRef.current = true; - // useFileUpload already has logic to auto-detect and convert .zip files - await onProcessFiles(event.target.files); - } - if (zipInputRef.current) zipInputRef.current.value = ""; - }, [onProcessFiles, justInitiatedFileOpRef, zipInputRef]); - - return { - handleFileChange, - handleFolderChange, - handleZipChange, - }; -}; diff --git a/all-model-chat/hooks/message-sender/standard/useApiInteraction.ts b/all-model-chat/hooks/message-sender/standard/useApiInteraction.ts deleted file mode 100644 index 2704ea80..00000000 --- a/all-model-chat/hooks/message-sender/standard/useApiInteraction.ts +++ /dev/null @@ -1,168 +0,0 @@ - -import React, { useCallback, Dispatch, SetStateAction } from 'react'; -import { AppSettings, ChatMessage, ChatSettings as IndividualChatSettings, UploadedFile } from '../../../types'; -import { createChatHistoryForApi, isGemini3Model, logService } from '../../../utils/appUtils'; -import { buildGenerationConfig } from '../../../services/api/baseApi'; -import { geminiServiceInstance } from '../../../services/geminiService'; -import { isLikelyHtml } from '../../../utils/codeUtils'; -import { GetStreamHandlers } from '../types'; -import { ContentPart } from '../../../types/chat'; - -interface UseApiInteractionProps { - appSettings: AppSettings; - messages: ChatMessage[]; - getStreamHandlers: GetStreamHandlers; - handleGenerateCanvas: (sourceMessageId: string, content: string) => Promise; - setSessionLoading: (sessionId: string, isLoading: boolean) => void; - activeJobs: React.MutableRefObject>; -} - -export const useApiInteraction = ({ - appSettings, - messages, - getStreamHandlers, - handleGenerateCanvas, - setSessionLoading, - activeJobs -}: UseApiInteractionProps) => { - - const performApiCall = useCallback(async (params: { - finalSessionId: string; - generationId: string; - generationStartTime: Date; - keyToUse: string; - activeModelId: string; - promptParts: ContentPart[]; - effectiveEditingId: string | null; - isContinueMode: boolean; - isRawMode: boolean; - sessionToUpdate: IndividualChatSettings; - aspectRatio: string; - imageSize: string | undefined; - newAbortController: AbortController; - textToUse: string; - enrichedFiles: UploadedFile[]; - }) => { - const { - finalSessionId, generationId, generationStartTime, keyToUse, activeModelId, - promptParts, effectiveEditingId, isContinueMode, isRawMode, - sessionToUpdate, aspectRatio, imageSize, newAbortController, - textToUse, enrichedFiles - } = params; - - let baseMessagesForApi: ChatMessage[] = messages; - - if (effectiveEditingId) { - const index = messages.findIndex(m => m.id === effectiveEditingId); - if (index !== -1) { - baseMessagesForApi = messages.slice(0, index); - } - } - - let finalRole: 'user' | 'model' = 'user'; - let finalParts = promptParts; - - if (isContinueMode) { - finalRole = 'model'; - const targetMsg = messages.find(m => m.id === effectiveEditingId); - const currentContent = targetMsg?.content || ''; - const isG3 = isGemini3Model(activeModelId); - - let prefillContent = currentContent; - if (!prefillContent.trim()) { - prefillContent = isG3 ? "I have finished reasoning" : " "; - } - finalParts = [{ text: prefillContent }]; - - } else if (isRawMode) { - const tempUserMsg: ChatMessage = { - id: 'temp-raw-user', - role: 'user', - content: textToUse.trim(), - files: enrichedFiles, - timestamp: new Date() - }; - baseMessagesForApi = [...baseMessagesForApi, tempUserMsg]; - - finalRole = 'model'; - finalParts = [{ text: '' }]; - - } else if (promptParts.length === 0) { - setSessionLoading(finalSessionId, false); - activeJobs.current.delete(generationId); - return; - } - - const shouldStripThinking = sessionToUpdate.hideThinkingInContext ?? appSettings.hideThinkingInContext; - const historyForChat = await createChatHistoryForApi(baseMessagesForApi, shouldStripThinking); - - const config = buildGenerationConfig( - activeModelId, - sessionToUpdate.systemInstruction, - { temperature: sessionToUpdate.temperature, topP: sessionToUpdate.topP }, - sessionToUpdate.showThoughts, - sessionToUpdate.thinkingBudget, - !!sessionToUpdate.isGoogleSearchEnabled, - !!sessionToUpdate.isCodeExecutionEnabled, - !!sessionToUpdate.isUrlContextEnabled, - sessionToUpdate.thinkingLevel, - aspectRatio, - sessionToUpdate.isDeepSearchEnabled, - imageSize, - sessionToUpdate.safetySettings, - sessionToUpdate.mediaResolution - ); - - const { streamOnError, streamOnComplete, streamOnPart, onThoughtChunk } = getStreamHandlers( - finalSessionId, - generationId, - newAbortController, - generationStartTime, - sessionToUpdate, - (msgId, content) => { - if (!isContinueMode && appSettings.autoCanvasVisualization && content && content.length > 50 && !isLikelyHtml(content)) { - const trimmed = content.trim(); - if (trimmed.startsWith('```') && trimmed.endsWith('```')) return; - logService.info("Auto-triggering Canvas visualization for message", { msgId }); - handleGenerateCanvas(msgId, content); - } - } - ); - - setSessionLoading(finalSessionId, true); - activeJobs.current.set(generationId, newAbortController); - - if (appSettings.isStreamingEnabled) { - await geminiServiceInstance.sendMessageStream( - keyToUse, - activeModelId, - historyForChat, - finalParts, - config, - newAbortController.signal, - streamOnPart, - onThoughtChunk, - streamOnError, - streamOnComplete, - finalRole - ); - } else { - await geminiServiceInstance.sendMessageNonStream( - keyToUse, - activeModelId, - historyForChat, - finalParts, - config, - newAbortController.signal, - streamOnError, - (parts, thoughts, usage, grounding) => { - for (const part of parts) streamOnPart(part); - if (thoughts) onThoughtChunk(thoughts); - streamOnComplete(usage, grounding); - } - ); - } - }, [appSettings, messages, getStreamHandlers, handleGenerateCanvas, setSessionLoading, activeJobs]); - - return { performApiCall }; -}; diff --git a/all-model-chat/package-lock.json b/all-model-chat/package-lock.json new file mode 100644 index 00000000..d49f53f0 --- /dev/null +++ b/all-model-chat/package-lock.json @@ -0,0 +1,13409 @@ +{ + "name": "all-model-chat", + "version": "1.8.5", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "all-model-chat", + "version": "1.8.5", + "workspaces": [ + "apps/*", + "packages/*" + ], + "dependencies": { + "@formkit/auto-animate": "^0.9.0", + "@google/genai": "^1.2.0", + "dompurify": "^3.1.6", + "highlight.js": "^11.9.0", + "html2canvas": "^1.4.1", + "jszip": "^3.10.1", + "katex": "^0.16.10", + "lucide-react": "^0.417.0", + "mammoth": "^1.6.0", + "marked": "^13.0.2", + "mermaid": "^10.9.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-markdown": "^9.0.1", + "react-pdf": "^10.3.0", + "react-virtuoso": "^4.18.1", + "rehype-highlight": "^7.0.0", + "rehype-katex": "^7.0.0", + "rehype-raw": "^7.0.0", + "rehype-sanitize": "^6.0.0", + "remark-breaks": "^4.0.0", + "remark-gfm": "^4.0.0", + "remark-math": "^6.0.0", + "turndown": "^7.2.0", + "turndown-plugin-gfm": "^1.0.2" + }, + "devDependencies": { + "@types/dompurify": "^3.0.5", + "@types/katex": "^0.16.7", + "@types/node": "^20.14.10", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@types/turndown": "^5.0.5", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.19", + "postcss": "^8.4.39", + "tailwindcss": "^3.4.4", + "typescript": "^5.5.3", + "vite": "^5.3.3" + } + }, + "apps/bff": { + "name": "@all-model-chat/bff", + "version": "1.8.5", + "dependencies": { + "@google/genai": "^1.2.0" + } + }, + "apps/web": { + "name": "@all-model-chat/web", + "version": "1.8.5" + }, + "node_modules/@all-model-chat/bff": { + "resolved": "apps/bff", + "link": true + }, + "node_modules/@all-model-chat/shared-api": { + "resolved": "packages/shared-api", + "link": true + }, + "node_modules/@all-model-chat/shared-config": { + "resolved": "packages/shared-config", + "link": true + }, + "node_modules/@all-model-chat/shared-types": { + "resolved": "packages/shared-types", + "link": true + }, + "node_modules/@all-model-chat/web": { + "resolved": "apps/web", + "link": true + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", + "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@formkit/auto-animate": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.9.0.tgz", + "integrity": "sha512-VhP4zEAacXS3dfTpJpJ88QdLqMTcabMg0jwpOSxZ/VzfQVfl3GkZSCZThhGC5uhq/TxPHPzW0dzr4H9Bb1OgKA==", + "license": "MIT" + }, + "node_modules/@google/genai": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.38.0.tgz", + "integrity": "sha512-V/4CQVQGovvGHuS73lwJwHKR9x33kCij3zz/ReEQ4A7RJaV0U7m4k1mvYhFk55cGZdF5JLKu2S9BTaFuEs5xTA==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mixmark-io/domino": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", + "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==", + "license": "BSD-2-Clause" + }, + "node_modules/@napi-rs/canvas": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.89.tgz", + "integrity": "sha512-7GjmkMirJHejeALCqUnZY3QwID7bbumOiLrqq2LKgxrdjdmxWQBTc6rcASa2u8wuWrH7qo4/4n/VNrOwCoKlKg==", + "license": "MIT", + "optional": true, + "workspaces": [ + "e2e/*" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.89", + "@napi-rs/canvas-darwin-arm64": "0.1.89", + "@napi-rs/canvas-darwin-x64": "0.1.89", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.89", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.89", + "@napi-rs/canvas-linux-arm64-musl": "0.1.89", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.89", + "@napi-rs/canvas-linux-x64-gnu": "0.1.89", + "@napi-rs/canvas-linux-x64-musl": "0.1.89", + "@napi-rs/canvas-win32-arm64-msvc": "0.1.89", + "@napi-rs/canvas-win32-x64-msvc": "0.1.89" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.89.tgz", + "integrity": "sha512-CXxQTXsjtQqKGENS8Ejv9pZOFJhOPIl2goenS+aU8dY4DygvkyagDhy/I07D1YLqrDtPvLEX5zZHt8qUdnuIpQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.89.tgz", + "integrity": "sha512-k29cR/Zl20WLYM7M8YePevRu2VQRaKcRedYr1V/8FFHkyIQ8kShEV+MPoPGi+znvmd17Eqjy2Pk2F2kpM2umVg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.89.tgz", + "integrity": "sha512-iUragqhBrA5FqU13pkhYBDbUD1WEAIlT8R2+fj6xHICY2nemzwMUI8OENDhRh7zuL06YDcRwENbjAVxOmaX9jg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.89.tgz", + "integrity": "sha512-y3SM9sfDWasY58ftoaI09YBFm35Ig8tosZqgahLJ2WGqawCusGNPV9P0/4PsrLOCZqGg629WxexQMY25n7zcvA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.89.tgz", + "integrity": "sha512-NEoF9y8xq5fX8HG8aZunBom1ILdTwt7ayBzSBIwrmitk7snj4W6Fz/yN/ZOmlM1iyzHDNX5Xn0n+VgWCF8BEdA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.89.tgz", + "integrity": "sha512-UQQkIEzV12/l60j1ziMjZ+mtodICNUbrd205uAhbyTw0t60CrC/EsKb5/aJWGq1wM0agvcgZV72JJCKfLS6+4w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.89.tgz", + "integrity": "sha512-1/VmEoFaIO6ONeeEMGoWF17wOYZOl5hxDC1ios2Bkz/oQjbJJ8DY/X22vWTmvuUKWWhBVlo63pxLGZbjJU/heA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.89.tgz", + "integrity": "sha512-ebLuqkCuaPIkKgKH9q4+pqWi1tkPOfiTk5PM1LKR1tB9iO9sFNVSIgwEp+SJreTSbA2DK5rW8lQXiN78SjtcvA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.89.tgz", + "integrity": "sha512-w+5qxHzplvA4BkHhCaizNMLLXiI+CfP84YhpHm/PqMub4u8J0uOAv+aaGv40rYEYra5hHRWr9LUd6cfW32o9/A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-arm64-msvc": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.89.tgz", + "integrity": "sha512-DmyXa5lJHcjOsDC78BM3bnEECqbK3xASVMrKfvtT/7S7Z8NGQOugvu+L7b41V6cexCd34mBWgMOsjoEBceeB1Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.89", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.89.tgz", + "integrity": "sha512-WMej0LZrIqIncQcx0JHaMXlnAG7sncwJh7obs/GBgp0xF9qABjwoRwIooMWCZkSansapKGNUHhamY6qEnFN7gA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.0.tgz", + "integrity": "sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.0.tgz", + "integrity": "sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.0.tgz", + "integrity": "sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.0.tgz", + "integrity": "sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.0.tgz", + "integrity": "sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.0.tgz", + "integrity": "sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.0.tgz", + "integrity": "sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.0.tgz", + "integrity": "sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.0.tgz", + "integrity": "sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.0.tgz", + "integrity": "sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.0.tgz", + "integrity": "sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.0.tgz", + "integrity": "sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.0.tgz", + "integrity": "sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.0.tgz", + "integrity": "sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.0.tgz", + "integrity": "sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.0.tgz", + "integrity": "sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.0.tgz", + "integrity": "sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.0.tgz", + "integrity": "sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.0.tgz", + "integrity": "sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.0.tgz", + "integrity": "sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.0.tgz", + "integrity": "sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.0.tgz", + "integrity": "sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.0.tgz", + "integrity": "sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.0.tgz", + "integrity": "sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.0.tgz", + "integrity": "sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", + "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", + "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/turndown": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/turndown/-/turndown-5.0.6.tgz", + "integrity": "sha512-ru00MoyeeouE5BX4gRL+6m/BsDfbRayOskWqUvh7CLGW+UXxHQItqALa38kKnOiZPqJrtzJUgAC2+F0rL1S4Pg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", + "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.23", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.18", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.18.tgz", + "integrity": "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001766", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", + "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/cytoscape": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", + "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz", + "integrity": "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==", + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dingbat-to-unicode": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz", + "integrity": "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w==", + "license": "BSD-2-Clause" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dompurify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/duck": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/duck/-/duck-0.1.12.tgz", + "integrity": "sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==", + "license": "BSD", + "dependencies": { + "underscore": "^1.13.1" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.279", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.279.tgz", + "integrity": "sha512-0bblUU5UNdOt5G7XqGiJtpZMONma6WAfq9vsFmtn9x1+joAObr6x1chfqyxFSDCAFwFhCQDrqeAr6MYdpwJ9Hg==", + "dev": true, + "license": "ISC" + }, + "node_modules/elkjs": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz", + "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==", + "license": "EPL-2.0" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/google-auth-library": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz", + "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^8.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-from-dom": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", + "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", + "license": "ISC", + "dependencies": { + "@types/hast": "^3.0.0", + "hastscript": "^9.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", + "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-from-html": "^2.0.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/hast-util-sanitize": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz", + "integrity": "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "unist-util-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/katex": { + "version": "0.16.28", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.28.tgz", + "integrity": "sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lop": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/lop/-/lop-0.4.2.tgz", + "integrity": "sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw==", + "license": "BSD-2-Clause", + "dependencies": { + "duck": "^0.1.12", + "option": "~0.2.1", + "underscore": "^1.13.1" + } + }, + "node_modules/lowlight": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz", + "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "highlight.js": "~11.11.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.417.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.417.0.tgz", + "integrity": "sha512-F/MDUHDter8YMZ7JKQpW/5/+v38tdaoShKX3e+opYsqfCnaHwn+5zz3+lBrMDFMNtSsvxtNpchLIaMpEfsi/4w==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/make-cancellable-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz", + "integrity": "sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw==", + "license": "MIT", + "funding": { + "url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1" + } + }, + "node_modules/make-event-props": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-2.0.0.tgz", + "integrity": "sha512-G/hncXrl4Qt7mauJEXSg3AcdYzmpkIITTNl5I+rH9sog5Yw0kK6vseJjCaPfOXqOqQuPUP89Rkhfz5kPS8ijtw==", + "license": "MIT", + "funding": { + "url": "https://github.com/wojtekmaj/make-event-props?sponsor=1" + } + }, + "node_modules/mammoth": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.11.0.tgz", + "integrity": "sha512-BcEqqY/BOwIcI1iR5tqyVlqc3KIaMRa4egSoK83YAVrBf6+yqdAAbtUcFDCWX8Zef8/fgNZ6rl4VUv+vVX8ddQ==", + "license": "BSD-2-Clause", + "dependencies": { + "@xmldom/xmldom": "^0.8.6", + "argparse": "~1.0.3", + "base64-js": "^1.5.1", + "bluebird": "~3.4.0", + "dingbat-to-unicode": "^1.0.1", + "jszip": "^3.7.1", + "lop": "^0.4.2", + "path-is-absolute": "^1.0.0", + "underscore": "^1.13.1", + "xmlbuilder": "^10.0.0" + }, + "bin": { + "mammoth": "bin/mammoth" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marked": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz", + "integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.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.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-gfm-footnote/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-footnote/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-gfm-table/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-table/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-gfm/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-gfm/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-math/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-math/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-math/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-math/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-math/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-math/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-math/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-mdx-expression/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdx-expression/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-mdx-jsx/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-newline-to-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-newline-to-break/-/mdast-util-newline-to-break-2.0.0.tgz", + "integrity": "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-find-and-replace": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-newline-to-break/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-to-markdown/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/merge-refs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz", + "integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==", + "license": "MIT", + "funding": { + "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "10.9.5", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.5.tgz", + "integrity": "sha512-eRlKEjzak4z1rcXeCd1OAlyawhrptClQDo8OuI8n6bSVqJ9oMfd5Lrf3Q+TdJHewi/9AIOc3UmEo8Fz+kNzzuQ==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^6.0.1", + "@types/d3-scale": "^4.0.3", + "@types/d3-scale-chromatic": "^3.0.0", + "cytoscape": "^3.28.1", + "cytoscape-cose-bilkent": "^4.1.0", + "d3": "^7.4.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.13", + "dayjs": "^1.11.7", + "dompurify": "^3.2.4", + "elkjs": "^0.9.0", + "katex": "^0.16.9", + "khroma": "^2.0.0", + "lodash-es": "^4.17.21", + "mdast-util-from-markdown": "^1.3.0", + "non-layered-tidy-tree-layout": "^2.0.2", + "stylis": "^4.1.3", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.0", + "web-worker": "^1.2.0" + } + }, + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-math/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/non-layered-tidy-tree-layout": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", + "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/option": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/option/-/option-0.2.4.tgz", + "integrity": "sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A==", + "license": "BSD-2-Clause" + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/pdfjs-dist": { + "version": "5.4.296", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz", + "integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=20.16.0 || >=22.3.0" + }, + "optionalDependencies": { + "@napi-rs/canvas": "^0.1.80" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-markdown": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.1.0.tgz", + "integrity": "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-markdown/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/react-pdf": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.3.0.tgz", + "integrity": "sha512-2LQzC9IgNVAX8gM+6F+1t/70a9/5RWThYxc+CWAmT2LW/BRmnj+35x1os5j/nR2oldyf8L+hCAMBmVKU8wrYFA==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "dequal": "^2.0.3", + "make-cancellable-promise": "^2.0.0", + "make-event-props": "^2.0.0", + "merge-refs": "^2.0.0", + "pdfjs-dist": "5.4.296", + "tiny-invariant": "^1.0.0", + "warning": "^4.0.0" + }, + "funding": { + "url": "https://github.com/wojtekmaj/react-pdf?sponsor=1" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-virtuoso": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.18.1.tgz", + "integrity": "sha512-KF474cDwaSb9+SJ380xruBB4P+yGWcVkcu26HtMqYNMTYlYbrNy8vqMkE+GpAApPPufJqgOLMoWMFG/3pJMXUA==", + "license": "MIT", + "peerDependencies": { + "react": ">=16 || >=17 || >= 18 || >= 19", + "react-dom": ">=16 || >=17 || >= 18 || >=19" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rehype-highlight": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rehype-highlight/-/rehype-highlight-7.0.2.tgz", + "integrity": "sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-text": "^4.0.0", + "lowlight": "^3.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-katex": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", + "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/katex": "^0.16.0", + "hast-util-from-html-isomorphic": "^2.0.0", + "hast-util-to-text": "^4.0.0", + "katex": "^0.16.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-sanitize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz", + "integrity": "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-sanitize": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-breaks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-4.0.0.tgz", + "integrity": "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-newline-to-break": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-breaks/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/remark-math": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", + "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-math/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/remark-parse/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/remark-parse/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/remark-parse/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/remark-parse/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-parse/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/remark-parse/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/remark-parse/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, + "node_modules/rollup": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.0.tgz", + "integrity": "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.0", + "@rollup/rollup-android-arm64": "4.57.0", + "@rollup/rollup-darwin-arm64": "4.57.0", + "@rollup/rollup-darwin-x64": "4.57.0", + "@rollup/rollup-freebsd-arm64": "4.57.0", + "@rollup/rollup-freebsd-x64": "4.57.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", + "@rollup/rollup-linux-arm-musleabihf": "4.57.0", + "@rollup/rollup-linux-arm64-gnu": "4.57.0", + "@rollup/rollup-linux-arm64-musl": "4.57.0", + "@rollup/rollup-linux-loong64-gnu": "4.57.0", + "@rollup/rollup-linux-loong64-musl": "4.57.0", + "@rollup/rollup-linux-ppc64-gnu": "4.57.0", + "@rollup/rollup-linux-ppc64-musl": "4.57.0", + "@rollup/rollup-linux-riscv64-gnu": "4.57.0", + "@rollup/rollup-linux-riscv64-musl": "4.57.0", + "@rollup/rollup-linux-s390x-gnu": "4.57.0", + "@rollup/rollup-linux-x64-gnu": "4.57.0", + "@rollup/rollup-linux-x64-musl": "4.57.0", + "@rollup/rollup-openbsd-x64": "4.57.0", + "@rollup/rollup-openharmony-arm64": "4.57.0", + "@rollup/rollup-win32-arm64-msvc": "4.57.0", + "@rollup/rollup-win32-ia32-msvc": "4.57.0", + "@rollup/rollup-win32-x64-gnu": "4.57.0", + "@rollup/rollup-win32-x64-msvc": "4.57.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/turndown": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.2.tgz", + "integrity": "sha512-1F7db8BiExOKxjSMU2b7if62D/XOyQyZbPKq/nUwopfgnHlqXHqQ0lvfUTeUIr1lZJzOPFn43dODyMSIfvWRKQ==", + "license": "MIT", + "dependencies": { + "@mixmark-io/domino": "^2.2.0" + } + }, + "node_modules/turndown-plugin-gfm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/turndown-plugin-gfm/-/turndown-plugin-gfm-1.0.2.tgz", + "integrity": "sha512-vwz9tfvF7XN/jE0dGoBei3FXWuvll78ohzCZQuOb+ZjWrs3a0XhQVomJEb2Qh4VHTPNRO4GPZh0V7VRbiWwkRg==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-find-after/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-visit/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/vfile-message/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/web-worker": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", + "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==", + "license": "Apache-2.0" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlbuilder": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", + "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "packages/shared-api": { + "name": "@all-model-chat/shared-api", + "version": "0.0.0" + }, + "packages/shared-config": { + "name": "@all-model-chat/shared-config", + "version": "0.0.0" + }, + "packages/shared-types": { + "name": "@all-model-chat/shared-types", + "version": "0.0.0" + } + } +} diff --git a/all-model-chat/package.json b/all-model-chat/package.json index 9c72cffc..f47bdb58 100644 --- a/all-model-chat/package.json +++ b/all-model-chat/package.json @@ -1,15 +1,25 @@ - { "name": "all-model-chat", "private": true, "version": "1.8.5", "type": "module", + "workspaces": [ + "apps/*", + "packages/*" + ], "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" + "dev": "npm run dev --workspace @all-model-chat/web", + "build": "npm run build --workspace @all-model-chat/web", + "preview": "npm run preview --workspace @all-model-chat/web", + "lint": "node scripts/lint-guards.mjs", + "typecheck": "npm run typecheck --workspaces --if-present", + "test": "npm run test --workspaces --if-present", + "ci:check": "npm run lint && npm run typecheck && npm run test && npm run build:workspaces", + "dev:workspaces": "npm run dev --workspaces --if-present", + "build:workspaces": "npm run build --workspaces --if-present" }, "dependencies": { + "@formkit/auto-animate": "^0.9.0", "@google/genai": "^1.2.0", "dompurify": "^3.1.6", "highlight.js": "^11.9.0", @@ -17,12 +27,14 @@ "jszip": "^3.10.1", "katex": "^0.16.10", "lucide-react": "^0.417.0", - "marked": "^13.0.2", "mammoth": "^1.6.0", + "marked": "^13.0.2", "mermaid": "^10.9.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^9.0.1", + "react-pdf": "^10.3.0", + "react-virtuoso": "^4.18.1", "rehype-highlight": "^7.0.0", "rehype-katex": "^7.0.0", "rehype-raw": "^7.0.0", @@ -47,4 +59,4 @@ "typescript": "^5.5.3", "vite": "^5.3.3" } -} \ No newline at end of file +} diff --git a/all-model-chat/packages/shared-api/package.json b/all-model-chat/packages/shared-api/package.json new file mode 100644 index 00000000..c31e5583 --- /dev/null +++ b/all-model-chat/packages/shared-api/package.json @@ -0,0 +1,7 @@ +{ + "name": "@all-model-chat/shared-api", + "private": true, + "version": "0.0.0", + "type": "module", + "main": "./src/index.ts" +} diff --git a/all-model-chat/packages/shared-api/src/index.d.ts b/all-model-chat/packages/shared-api/src/index.d.ts new file mode 100644 index 00000000..3e5abf33 --- /dev/null +++ b/all-model-chat/packages/shared-api/src/index.d.ts @@ -0,0 +1,143 @@ +import type { Part, UsageMetadata } from '@google/genai'; +import type { AppLanguage, ChatRole } from '@all-model-chat/shared-types'; + +export interface ApiErrorPayload { + code: string; + message: string; + status: number; + retryable: boolean; +} + +export interface BffErrorPayload { + code?: string; + message?: string; + status?: number; + retryable?: boolean; +} + +export interface ChatHistoryTurn { + role: ChatRole; + parts: Part[]; +} + +export interface ChatStreamRequestPayload { + model: string; + history: ChatHistoryTurn[]; + parts: Part[]; + config?: unknown; + role: ChatRole; + apiKeyOverride?: string; +} + +export interface ChatStreamMetaEventPayload { + provider: 'gemini'; + keyId: string; +} + +export interface ChatStreamPartEventPayload { + part: Part; +} + +export interface ChatStreamThoughtEventPayload { + chunk: string; +} + +export interface ChatStreamCompleteEventPayload { + usageMetadata?: UsageMetadata; + groundingMetadata?: unknown; + urlContextMetadata?: unknown; + functionCallPart?: Part; +} + +export interface ChatStreamErrorEventPayload { + error: ApiErrorPayload; +} + +export interface ImageGenerationRequest { + model: string; + prompt: string; + aspectRatio: string; + imageSize?: string; +} + +export interface ImageGenerationResponse { + images: string[]; +} + +export interface SpeechGenerationRequest { + model: string; + text: string; + voice: string; +} + +export interface SpeechGenerationResponse { + audioData: string; +} + +export interface TranscribeAudioRequest { + model: string; + mimeType: string; + audioBase64: string; +} + +export interface TranscribeAudioResponse { + text: string; +} + +export interface TranslateRequest { + text: string; + targetLanguage?: string; +} + +export interface TranslateResponse { + text: string; +} + +export interface TitleRequest { + userContent: string; + modelContent: string; + language: AppLanguage; +} + +export interface TitleResponse { + title: string; +} + +export interface SuggestionsRequest { + userContent: string; + modelContent: string; + language: AppLanguage; +} + +export interface SuggestionsResponse { + suggestions: string[]; +} + +export interface CountTokensRequest { + model: string; + parts: Part[]; +} + +export interface CountTokensResponse { + totalTokens: number; +} + +export interface EditImageRequest { + model: string; + history: ChatHistoryTurn[]; + parts: Part[]; + aspectRatio?: string; + imageSize?: string; +} + +export interface EditImageResponse { + parts: Part[]; +} + +export interface FileUploadResponse { + file: TFile; +} + +export interface FileMetadataResponse { + file: TFile | null; +} diff --git a/all-model-chat/packages/shared-api/src/index.ts b/all-model-chat/packages/shared-api/src/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/all-model-chat/packages/shared-api/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/all-model-chat/packages/shared-config/package.json b/all-model-chat/packages/shared-config/package.json new file mode 100644 index 00000000..e545f9ff --- /dev/null +++ b/all-model-chat/packages/shared-config/package.json @@ -0,0 +1,6 @@ +{ + "name": "@all-model-chat/shared-config", + "private": true, + "version": "0.0.0", + "type": "module" +} diff --git a/all-model-chat/packages/shared-types/package.json b/all-model-chat/packages/shared-types/package.json new file mode 100644 index 00000000..19cdcacc --- /dev/null +++ b/all-model-chat/packages/shared-types/package.json @@ -0,0 +1,7 @@ +{ + "name": "@all-model-chat/shared-types", + "private": true, + "version": "0.0.0", + "type": "module", + "main": "./src/index.ts" +} diff --git a/all-model-chat/packages/shared-types/src/index.d.ts b/all-model-chat/packages/shared-types/src/index.d.ts new file mode 100644 index 00000000..58786b73 --- /dev/null +++ b/all-model-chat/packages/shared-types/src/index.d.ts @@ -0,0 +1,3 @@ +export type ChatRole = 'user' | 'model'; + +export type AppLanguage = 'en' | 'zh'; diff --git a/all-model-chat/packages/shared-types/src/index.ts b/all-model-chat/packages/shared-types/src/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/all-model-chat/packages/shared-types/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/all-model-chat/scripts/lint-guards.mjs b/all-model-chat/scripts/lint-guards.mjs new file mode 100644 index 00000000..52f9717c --- /dev/null +++ b/all-model-chat/scripts/lint-guards.mjs @@ -0,0 +1,40 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +const rootDir = process.cwd(); + +const checks = [ + { + file: 'apps/web/index.html', + pattern: //m, + message: 'importmap runtime mapping is not allowed in web index.html.', + }, + { + file: 'apps/web/index.html', + pattern: //m, + message: 'stale /index.css entry reference detected in web index.html.', + }, + { + file: 'apps/web/utils/apiUtils.ts', + pattern: /recordApiKeyUsage\(/m, + message: 'raw key usage tracking call detected in apiUtils.ts.', + }, +]; + +let hasFailure = false; + +for (const check of checks) { + const targetPath = path.join(rootDir, check.file); + const content = fs.readFileSync(targetPath, 'utf8'); + + if (check.pattern.test(content)) { + hasFailure = true; + console.error(`[lint-guards] ${check.file}: ${check.message}`); + } +} + +if (hasFailure) { + process.exit(1); +} + +console.log('[lint-guards] all guard checks passed.'); diff --git a/all-model-chat/services/api/chatApi.ts b/all-model-chat/services/api/chatApi.ts deleted file mode 100644 index 029d2499..00000000 --- a/all-model-chat/services/api/chatApi.ts +++ /dev/null @@ -1,186 +0,0 @@ - -import { GenerateContentResponse, Part, UsageMetadata, ChatHistoryItem } from "@google/genai"; -import { ThoughtSupportingPart } from '../../types'; -import { logService } from "../logService"; -import { getConfiguredApiClient } from "./baseApi"; - -/** - * Shared helper to parse GenAI responses. - * Extracts parts, separates thoughts, and merges metadata/citations from tool calls. - */ -const processResponse = (response: GenerateContentResponse) => { - let thoughtsText = ""; - const responseParts: Part[] = []; - - if (response.candidates && response.candidates[0]?.content?.parts) { - for (const part of response.candidates[0].content.parts) { - const pAsThoughtSupporting = part as ThoughtSupportingPart; - if (pAsThoughtSupporting.thought) { - thoughtsText += part.text; - } else { - responseParts.push(part); - } - } - } - - if (responseParts.length === 0 && response.text) { - responseParts.push({ text: response.text }); - } - - const candidate = response.candidates?.[0]; - const groundingMetadata = candidate?.groundingMetadata; - const finalMetadata: any = groundingMetadata ? { ...groundingMetadata } : {}; - - // @ts-ignore - Handle potential snake_case from raw API responses - const urlContextMetadata = candidate?.urlContextMetadata || candidate?.url_context_metadata; - - const toolCalls = candidate?.toolCalls; - if (toolCalls) { - for (const toolCall of toolCalls) { - if (toolCall.functionCall?.args?.urlContextMetadata) { - if (!finalMetadata.citations) finalMetadata.citations = []; - const newCitations = toolCall.functionCall.args.urlContextMetadata.citations || []; - for (const newCitation of newCitations) { - if (!finalMetadata.citations.some((c: any) => c.uri === newCitation.uri)) { - finalMetadata.citations.push(newCitation); - } - } - } - } - } - - return { - parts: responseParts, - thoughts: thoughtsText || undefined, - usage: response.usageMetadata, - grounding: Object.keys(finalMetadata).length > 0 ? finalMetadata : undefined, - urlContext: urlContextMetadata - }; -}; - -export const sendStatelessMessageStreamApi = async ( - apiKey: string, - modelId: string, - history: ChatHistoryItem[], - parts: Part[], - config: any, - abortSignal: AbortSignal, - onPart: (part: Part) => void, - onThoughtChunk: (chunk: string) => void, - onError: (error: Error) => void, - onComplete: (usageMetadata?: UsageMetadata, groundingMetadata?: any, urlContextMetadata?: any) => void, - role: 'user' | 'model' = 'user' -): Promise => { - logService.info(`Sending message via stateless generateContentStream for ${modelId} (Role: ${role})`); - let finalUsageMetadata: UsageMetadata | undefined = undefined; - let finalGroundingMetadata: any = null; - let finalUrlContextMetadata: any = null; - - try { - const ai = await getConfiguredApiClient(apiKey); - - if (abortSignal.aborted) { - logService.warn("Streaming aborted by signal before start."); - return; - } - - const result = await ai.models.generateContentStream({ - model: modelId, - contents: [...history, { role: role, parts }], - config: config - }); - - for await (const chunkResponse of result) { - if (abortSignal.aborted) { - logService.warn("Streaming aborted by signal."); - break; - } - if (chunkResponse.usageMetadata) { - finalUsageMetadata = chunkResponse.usageMetadata; - } - const candidate = chunkResponse.candidates?.[0]; - - if (candidate) { - const metadataFromChunk = candidate.groundingMetadata; - if (metadataFromChunk) { - finalGroundingMetadata = metadataFromChunk; - } - - // @ts-ignore - const urlMetadata = candidate.urlContextMetadata || candidate.url_context_metadata; - if (urlMetadata) { - finalUrlContextMetadata = urlMetadata; - } - - const toolCalls = candidate.toolCalls; - if (toolCalls) { - for (const toolCall of toolCalls) { - if (toolCall.functionCall?.args?.urlContextMetadata) { - if (!finalGroundingMetadata) finalGroundingMetadata = {}; - if (!finalGroundingMetadata.citations) finalGroundingMetadata.citations = []; - const newCitations = toolCall.functionCall.args.urlContextMetadata.citations || []; - for (const newCitation of newCitations) { - if (!finalGroundingMetadata.citations.some((c: any) => c.uri === newCitation.uri)) { - finalGroundingMetadata.citations.push(newCitation); - } - } - } - } - } - - if (candidate.content?.parts?.length) { - for (const part of candidate.content.parts) { - const pAsThoughtSupporting = part as ThoughtSupportingPart; - - if (pAsThoughtSupporting.thought) { - onThoughtChunk(part.text || ''); - } else { - onPart(part); - } - } - } - } - } - } catch (error) { - logService.error("Error sending message (stream):", error); - onError(error instanceof Error ? error : new Error(String(error) || "Unknown error during streaming.")); - } finally { - logService.info("Streaming complete.", { usage: finalUsageMetadata, hasGrounding: !!finalGroundingMetadata }); - onComplete(finalUsageMetadata, finalGroundingMetadata, finalUrlContextMetadata); - } -}; - -export const sendStatelessMessageNonStreamApi = async ( - apiKey: string, - modelId: string, - history: ChatHistoryItem[], - parts: Part[], - config: any, - abortSignal: AbortSignal, - onError: (error: Error) => void, - onComplete: (parts: Part[], thoughtsText?: string, usageMetadata?: UsageMetadata, groundingMetadata?: any, urlContextMetadata?: any) => void -): Promise => { - logService.info(`Sending message via stateless generateContent (non-stream) for model ${modelId}`); - - try { - const ai = await getConfiguredApiClient(apiKey); - - if (abortSignal.aborted) { onComplete([], "", undefined, undefined, undefined); return; } - - const response = await ai.models.generateContent({ - model: modelId, - contents: [...history, { role: 'user', parts }], - config: config - }); - - if (abortSignal.aborted) { onComplete([], "", undefined, undefined, undefined); return; } - - const { parts: responseParts, thoughts, usage, grounding, urlContext } = processResponse(response); - - logService.info(`Stateless non-stream complete for ${modelId}.`, { usage, hasGrounding: !!grounding, hasUrlContext: !!urlContext }); - onComplete(responseParts, thoughts, usage, grounding, urlContext); - } catch (error) { - logService.error(`Error in stateless non-stream for ${modelId}:`, error); - onError(error instanceof Error ? error : new Error(String(error) || "Unknown error during stateless non-streaming call.")); - } -}; diff --git a/all-model-chat/services/api/generation/audioApi.ts b/all-model-chat/services/api/generation/audioApi.ts deleted file mode 100644 index 9d6d72ad..00000000 --- a/all-model-chat/services/api/generation/audioApi.ts +++ /dev/null @@ -1,126 +0,0 @@ - - -import { getConfiguredApiClient } from '../baseApi'; -import { logService } from "../../logService"; -import { Part } from "@google/genai"; -import { blobToBase64 } from "../../../utils/appUtils"; - -export const generateSpeechApi = async (apiKey: string, modelId: string, text: string, voice: string, abortSignal: AbortSignal): Promise => { - logService.info(`Generating speech with model ${modelId}`, { textLength: text.length, voice }); - - if (!text.trim()) { - throw new Error("TTS input text cannot be empty."); - } - - try { - const ai = await getConfiguredApiClient(apiKey); - const response = await ai.models.generateContent({ - model: modelId, - // TTS models do not support chat history roles, just plain content parts - contents: [{ parts: [{ text: text }] }], - config: { - responseModalities: ['AUDIO'], - speechConfig: { - voiceConfig: { prebuiltVoiceConfig: { voiceName: voice } }, - }, - }, - }); - - if (abortSignal.aborted) { - const abortError = new Error("Speech generation cancelled by user."); - abortError.name = "AbortError"; - throw abortError; - } - - const audioData = response.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data; - - if (typeof audioData === 'string' && audioData.length > 0) { - return audioData; - } - - const candidate = response.candidates?.[0]; - if (candidate?.finishReason && candidate.finishReason !== 'STOP') { - throw new Error(`TTS generation failed with reason: ${candidate.finishReason}`); - } - - logService.error("TTS response did not contain expected audio data structure:", { response }); - - // Fallback to checking text error if any, though unlikely with AUDIO modality - const textError = response.text; - if (textError) { - throw new Error(`TTS generation failed: ${textError}`); - } - - throw new Error('No audio data found in TTS response.'); - - } catch (error) { - logService.error(`Failed to generate speech with model ${modelId}:`, error); - throw error; - } -}; - -export const transcribeAudioApi = async (apiKey: string, audioFile: File, modelId: string): Promise => { - logService.info(`Transcribing audio with model ${modelId}`, { fileName: audioFile.name, size: audioFile.size }); - - try { - const ai = await getConfiguredApiClient(apiKey); - // Use blobToBase64 which is efficient and handles Blobs/Files - const audioBase64 = await blobToBase64(audioFile); - - const audioPart: Part = { - inlineData: { - mimeType: audioFile.type, - data: audioBase64, - }, - }; - - const textPart: Part = { - text: "Transcribe audio.", - }; - - const config: any = { - systemInstruction: "请准确转录语音内容。使用正确的标点符号。不要描述音频、回答问题或添加对话填充词,仅返回文本。若音频中无语音或仅有背景噪音,请不要输出任何文字。", - }; - - // Apply specific defaults based on model - if (modelId.includes('gemini-3')) { - config.thinkingConfig = { - includeThoughts: false, - thinkingLevel: "MINIMAL" - }; - } else if (modelId === 'gemini-2.5-pro') { - config.thinkingConfig = { - thinkingBudget: 128, - }; - } else if (modelId.includes('flash')) { - // Both 2.5 Flash and Flash Lite - config.thinkingConfig = { - thinkingBudget: 512, - }; - } else { - // Disable thinking for other models by default - config.thinkingConfig = { - thinkingBudget: 0, - }; - } - - const response = await ai.models.generateContent({ - model: modelId, - contents: { parts: [textPart, audioPart] }, - config, - }); - - if (response.text) { - return response.text; - } else { - const safetyFeedback = response.candidates?.[0]?.finishReason; - if (safetyFeedback && safetyFeedback !== 'STOP') { - throw new Error(`Transcription failed due to safety settings: ${safetyFeedback}`); - } - throw new Error("Transcription failed. The model returned an empty response."); - } - } catch (error) { - logService.error("Error during audio transcription:", error); - throw error; - } -}; \ No newline at end of file diff --git a/all-model-chat/services/api/generation/imageApi.ts b/all-model-chat/services/api/generation/imageApi.ts deleted file mode 100644 index 0f084beb..00000000 --- a/all-model-chat/services/api/generation/imageApi.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { getConfiguredApiClient } from '../baseApi'; -import { logService } from "../../logService"; - -export const generateImagesApi = async (apiKey: string, modelId: string, prompt: string, aspectRatio: string, imageSize: string | undefined, abortSignal: AbortSignal): Promise => { - logService.info(`Generating image with model ${modelId}`, { prompt, aspectRatio, imageSize }); - - if (!prompt.trim()) { - throw new Error("Image generation prompt cannot be empty."); - } - - if (abortSignal.aborted) { - const abortError = new Error("Image generation cancelled by user before starting."); - abortError.name = "AbortError"; - throw abortError; - } - - try { - const ai = await getConfiguredApiClient(apiKey); - const config: any = { - numberOfImages: 1, - outputMimeType: 'image/png', - aspectRatio: aspectRatio - }; - - if (imageSize) { - config.imageSize = imageSize; - } - - const response = await ai.models.generateImages({ - model: modelId, - prompt: prompt, - config: config, - }); - - if (abortSignal.aborted) { - const abortError = new Error("Image generation cancelled by user."); - abortError.name = "AbortError"; - throw abortError; - } - - const images = response.generatedImages?.map(img => img.image.imageBytes) ?? []; - if (images.length === 0) { - throw new Error("No images generated. The prompt may have been blocked or the model failed to respond."); - } - - return images; - - } catch (error) { - logService.error(`Failed to generate images with model ${modelId}:`, error); - throw error; - } -}; diff --git a/all-model-chat/services/api/generation/textApi.ts b/all-model-chat/services/api/generation/textApi.ts deleted file mode 100644 index 45137d19..00000000 --- a/all-model-chat/services/api/generation/textApi.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { getConfiguredApiClient } from '../baseApi'; -import { logService } from "../../logService"; -import { Type } from "@google/genai"; - -export const translateTextApi = async (apiKey: string, text: string, targetLanguage: string = 'English'): Promise => { - logService.info(`Translating text to ${targetLanguage}...`); - const prompt = `Translate the following text to ${targetLanguage}. Only return the translated text, without any additional explanation or formatting.\n\nText to translate:\n"""\n${text}\n"""`; - - try { - const ai = await getConfiguredApiClient(apiKey); - const response = await ai.models.generateContent({ - model: 'gemini-2.5-flash-lite', - contents: prompt, - config: { - temperature: 0.1, - topP: 0.95, - thinkingConfig: { thinkingBudget: -1 }, - } - }); - - if (response.text) { - return response.text.trim(); - } else { - throw new Error("Translation failed. The model returned an empty response."); - } - } catch (error) { - logService.error("Error during text translation:", error); - throw error; - } -}; - -export const generateSuggestionsApi = async (apiKey: string, userContent: string, modelContent: string, language: 'en' | 'zh'): Promise => { - logService.info(`Generating suggestions in ${language}...`); - - const prompt = language === 'zh' - ? `作为对话专家,请基于以下上下文,预测用户接下来最可能发送的 3 条简短回复。 - -规则: -1. 如果助手最后在提问,建议必须是针对该问题的回答。 -2. 建议应简练(20字以内),涵盖不同角度(如:追问细节、请求示例、或提出质疑)。 -3. 语气自然,符合人类对话习惯。 - -对话上下文: -用户: "${userContent}" -助手: "${modelContent}"` - : `As a conversation expert, predict the 3 most likely short follow-up messages the USER would send based on the context below. - -Context: -USER: "${userContent}" -ASSISTANT: "${modelContent}"`; - - try { - const ai = await getConfiguredApiClient(apiKey); - const response = await ai.models.generateContent({ - model: 'gemini-2.5-flash-lite', - contents: prompt, - config: { - thinkingConfig: { thinkingBudget: -1 }, // auto - temperature: 0.8, - topP: 0.95, - responseMimeType: "application/json", - responseSchema: { - type: Type.OBJECT, - properties: { - suggestions: { - type: Type.ARRAY, - items: { - type: Type.STRING, - description: "A short, relevant suggested reply or follow-up question." - }, - description: "An array of exactly three suggested replies." - } - } - } - } - }); - - const jsonStr = response.text.trim(); - const parsed = JSON.parse(jsonStr); - if (parsed.suggestions && Array.isArray(parsed.suggestions) && parsed.suggestions.every((s: any) => typeof s === 'string')) { - return parsed.suggestions.slice(0, 3); // Ensure only 3 - } else { - throw new Error("Suggestions generation returned an invalid format."); - } - } catch (error) { - logService.error("Error during suggestions generation:", error); - // Fallback to a non-JSON approach in case the model struggles with the schema - try { - const ai = await getConfiguredApiClient(apiKey); // Re-get client - const fallbackPrompt = `${prompt}\n\nReturn the three suggestions as a numbered list, one per line. Do not include any other text or formatting.`; - const fallbackResponse = await ai.models.generateContent({ - model: 'gemini-2.5-flash-lite', - contents: fallbackPrompt, - config: { - thinkingConfig: { thinkingBudget: -1 }, - temperature: 0.8, - topP: 0.95, - } - }); - if (fallbackResponse.text) { - return fallbackResponse.text.trim().split('\n').map(s => s.replace(/^\d+\.\s*/, '').trim()).filter(Boolean).slice(0, 3); - } - } catch (fallbackError) { - logService.error("Fallback suggestions generation also failed:", fallbackError); - } - return []; // Return empty array on failure - } -}; - -export const generateTitleApi = async (apiKey: string, userContent: string, modelContent: string, language: 'en' | 'zh'): Promise => { - logService.info(`Generating title in ${language}...`); - const prompt = language === 'zh' - ? `根据以下对话,创建一个非常简短、简洁的标题(最多4-6个词)。不要使用引号或任何其他格式。只返回标题的文本。\n\n用户: "${userContent}"\n助手: "${modelContent}"\n\n标题:` - : `Based on this conversation, create a very short, concise title (4-6 words max). Do not use quotes or any other formatting. Just return the text of the title.\n\nUSER: "${userContent}"\nASSISTANT: "${modelContent}"\n\nTITLE:`; - - try { - const ai = await getConfiguredApiClient(apiKey); - const response = await ai.models.generateContent({ - model: 'gemini-2.5-flash-lite', - contents: prompt, - config: { - thinkingConfig: { thinkingBudget: -1 }, - temperature: 0.3, - topP: 0.9, - } - }); - - if (response.text) { - // Clean up the title: remove quotes, trim whitespace - let title = response.text.trim(); - if ((title.startsWith('"') && title.endsWith('"')) || (title.startsWith("'") && title.endsWith("'"))) { - title = title.substring(1, title.length - 1); - } - return title; - } else { - throw new Error("Title generation failed. The model returned an empty response."); - } - } catch (error) { - logService.error("Error during title generation:", error); - throw error; - } -}; diff --git a/all-model-chat/services/api/generation/tokenApi.ts b/all-model-chat/services/api/generation/tokenApi.ts deleted file mode 100644 index 9711a8d7..00000000 --- a/all-model-chat/services/api/generation/tokenApi.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import { getConfiguredApiClient } from '../baseApi'; -import { logService } from "../../logService"; -import { Part } from "@google/genai"; - -export const countTokensApi = async (apiKey: string, modelId: string, parts: Part[]): Promise => { - logService.info(`Counting tokens for model ${modelId}...`); - try { - const ai = await getConfiguredApiClient(apiKey); - - // Sanitize parts to remove custom properties that might cause API errors in countTokens. - // Properties like mediaResolution, videoMetadata, or thoughtSignature might not be supported in the countTokens endpoint payload - // or strictly validated, causing 400 errors. - const sanitizedParts = parts.map(p => { - // Create a shallow copy to avoid mutating the original array elements - // Explicitly exclude known custom extended fields - const { mediaResolution, videoMetadata, thoughtSignature, ...rest } = p as any; - return rest as Part; - }); - - const response = await ai.models.countTokens({ - model: modelId, - contents: [{ role: 'user', parts: sanitizedParts }] - }); - return response.totalTokens || 0; - } catch (error) { - logService.error("Error counting tokens:", error); - throw error; - } -}; diff --git a/all-model-chat/tsconfig.base.json b/all-model-chat/tsconfig.base.json new file mode 100644 index 00000000..00cc4d99 --- /dev/null +++ b/all-model-chat/tsconfig.base.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2022", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "ESNext", + "lib": [ + "ES2022", + "DOM", + "DOM.Iterable" + ], + "skipLibCheck": true, + "moduleResolution": "bundler", + "isolatedModules": true, + "moduleDetection": "force", + "allowJs": true, + "jsx": "react-jsx", + "allowImportingTsExtensions": true, + "noEmit": true, + "baseUrl": ".", + "paths": { + "@all-model-chat/shared-api": [ + "packages/shared-api/src/index.d.ts" + ], + "@all-model-chat/shared-types": [ + "packages/shared-types/src/index.d.ts" + ] + } + } +} diff --git a/all-model-chat/tsconfig.json b/all-model-chat/tsconfig.json index 4ab74054..11fa6296 100644 --- a/all-model-chat/tsconfig.json +++ b/all-model-chat/tsconfig.json @@ -1,26 +1,10 @@ { + "extends": "./tsconfig.base.json", "compilerOptions": { - "target": "ES2022", - "experimentalDecorators": true, - "useDefineForClassFields": false, - "module": "ESNext", - "lib": [ - "ES2022", - "DOM", - "DOM.Iterable" - ], - "skipLibCheck": true, - "moduleResolution": "bundler", - "isolatedModules": true, - "moduleDetection": "force", - "allowJs": true, - "jsx": "react-jsx", "paths": { "@/*": [ "./*" ] - }, - "allowImportingTsExtensions": true, - "noEmit": true + } } -} \ No newline at end of file +} diff --git a/all-model-chat/utils/chat/builder.ts b/all-model-chat/utils/chat/builder.ts deleted file mode 100644 index e8fcb9cf..00000000 --- a/all-model-chat/utils/chat/builder.ts +++ /dev/null @@ -1,185 +0,0 @@ - -import { ChatMessage, ContentPart, UploadedFile, ChatHistoryItem } from '../../types'; -import { SUPPORTED_TEXT_MIME_TYPES, TEXT_BASED_EXTENSIONS } from '../../constants/fileConstants'; -import { logService } from '../../services/logService'; -import { blobToBase64, fileToString } from '../fileHelpers'; -import { isGemini3Model } from '../modelHelpers'; -import { MediaResolution } from '../../types/settings'; - -export const buildContentParts = async ( - text: string, - files: UploadedFile[] | undefined, - modelId?: string, - mediaResolution?: MediaResolution -): Promise<{ - contentParts: ContentPart[]; - enrichedFiles: UploadedFile[]; -}> => { - const filesToProcess = files || []; - - // Check if model supports per-part resolution (Gemini 3 family) - const isGemini3 = modelId && isGemini3Model(modelId); - - const processedResults = await Promise.all(filesToProcess.map(async (file) => { - const newFile = { ...file }; - let part: ContentPart | null = null; - - if (file.isProcessing || file.error || file.uploadState !== 'active') { - return { file: newFile, part }; - } - - const isVideo = file.type.startsWith('video/'); - const isYoutube = file.type === 'video/youtube-link'; - // Check if file should be treated as text content (not base64 inlineData) - const fileExtension = `.${file.name.split('.').pop()?.toLowerCase()}`; - const isTextLike = SUPPORTED_TEXT_MIME_TYPES.includes(file.type) || TEXT_BASED_EXTENSIONS.includes(fileExtension) || file.type === 'text/plain'; - - if (file.fileUri) { - // 1. Files uploaded via API (or YouTube links) - if (isYoutube) { - // For YouTube URLs, do NOT send mimeType, just fileUri. - part = { fileData: { fileUri: file.fileUri } }; - } else { - part = { fileData: { mimeType: file.type, fileUri: file.fileUri } }; - } - } else { - // 2. Files NOT uploaded via API (Inline handling) - const fileSource = file.rawFile; - const urlSource = file.dataUrl; - - if (isTextLike) { - // Special handling for text/code: Read content and wrap in text part - let textContent = ''; - if (fileSource && (fileSource instanceof File || fileSource instanceof Blob)) { - // If it's a File/Blob, read directly - textContent = await fileToString(fileSource as File); - } else if (urlSource) { - // Fallback: Fetch from URL if rawFile is missing - const response = await fetch(urlSource); - textContent = await response.text(); - } - if (textContent) { - // Format as a pseudo-file block for the model - part = { text: `\n--- START OF FILE ${file.name} ---\n${textContent}\n--- END OF FILE ${file.name} ---\n` }; - } - } else { - // Standard Inline Data (Images, PDFs, Audio, Video if small enough) - // STRICT ALLOWLIST for Inline Data to prevent API 400 errors (e.g. for .xlsx) - const isSupportedInlineType = - file.type.startsWith('image/') || - file.type.startsWith('audio/') || - file.type.startsWith('video/') || - file.type === 'application/pdf'; - - if (isSupportedInlineType) { - let base64DataForApi: string | undefined; - - // Prioritize rawFile (Blob/File) for conversion - if (fileSource && (fileSource instanceof Blob || fileSource instanceof File)) { - try { - base64DataForApi = await blobToBase64(fileSource); - } catch (error) { - logService.error(`Failed to convert rawFile to base64 for ${file.name}`, { error }); - } - } else if (urlSource) { - // Fallback: Fetch the blob from the URL (blob: or data:) - try { - const response = await fetch(urlSource); - const blob = await response.blob(); - base64DataForApi = await blobToBase64(blob); - - // Self-repair: If we had to fetch because rawFile was missing, recreate it - if (!newFile.rawFile) { - newFile.rawFile = new File([blob], file.name, { type: file.type }); - } - } catch (error) { - logService.error(`Failed to fetch blob and convert to base64 for ${file.name}`, { error }); - } - } - - if (base64DataForApi) { - part = { inlineData: { mimeType: file.type, data: base64DataForApi } }; - } - } else { - // Fallback for unsupported binary types - part = { text: `[Attachment: ${file.name} (Binary content not supported for direct reading)]` }; - } - } - } - - // Inject video metadata if present and it's a video (works for both inline and fileUri video/youtube) - if (part && (isVideo || isYoutube) && file.videoMetadata) { - part.videoMetadata = { ...part.videoMetadata }; // Ensure object exists - - if (file.videoMetadata.startOffset) { - part.videoMetadata.startOffset = file.videoMetadata.startOffset; - } - if (file.videoMetadata.endOffset) { - part.videoMetadata.endOffset = file.videoMetadata.endOffset; - } - if (file.videoMetadata.fps) { - part.videoMetadata.fps = file.videoMetadata.fps; - } - } - - // Inject Per-Part Media Resolution (Gemini 3 feature) - // Only apply to supported media types (images, videos, pdfs) not text/code - // Prioritize file-level resolution, then global resolution - const effectiveResolution = file.mediaResolution || mediaResolution; - - if (part && isGemini3 && effectiveResolution && effectiveResolution !== MediaResolution.MEDIA_RESOLUTION_UNSPECIFIED) { - const shouldInject = (part.fileData && !isYoutube) || (part.inlineData && !isTextLike); - if (shouldInject) { - part.mediaResolution = { level: effectiveResolution }; - } - } - - return { file: newFile, part }; - })); - - const enrichedFiles = processedResults.map(r => r.file); - const dataParts = processedResults.map(r => r.part).filter((p): p is ContentPart => p !== null); - - const userTypedText = text.trim(); - const contentPartsResult: ContentPart[] = []; - - // Optimize: Place media parts first as recommended by Gemini documentation for better multimodal performance - contentPartsResult.push(...dataParts); - - if (userTypedText) { - contentPartsResult.push({ text: userTypedText }); - } - - return { contentParts: contentPartsResult, enrichedFiles }; -}; - -export const createChatHistoryForApi = async ( - msgs: ChatMessage[], - stripThinking: boolean = false -): Promise => { - const historyItemsPromises = msgs - .filter(msg => (msg.role === 'user' || msg.role === 'model') && !msg.excludeFromContext) - .map(async (msg) => { - let contentToUse = msg.content; - - if (stripThinking) { - // Remove blocks including tags from the content - contentToUse = contentToUse.replace(/[\s\S]*?<\/[^>]+>/gi, '').trim(); - } - - // Use buildContentParts for both user and model messages to handle text and files consistently. - const { contentParts } = await buildContentParts(contentToUse, msg.files); - - // Attach Thought Signatures if present (Crucial for Gemini 3 Pro) - if (msg.role === 'model' && msg.thoughtSignatures && msg.thoughtSignatures.length > 0) { - if (contentParts.length > 0) { - const lastPart = contentParts[contentParts.length - 1]; - lastPart.thoughtSignature = msg.thoughtSignatures[msg.thoughtSignatures.length - 1]; - } - } - - return { role: msg.role as 'user' | 'model', parts: contentParts }; - }); - - return Promise.all(historyItemsPromises); -}; \ No newline at end of file diff --git a/all-model-chat/utils/folderImportUtils.ts b/all-model-chat/utils/folderImportUtils.ts deleted file mode 100644 index 5b8d9b0b..00000000 --- a/all-model-chat/utils/folderImportUtils.ts +++ /dev/null @@ -1,205 +0,0 @@ - -import { fileToString } from './domainUtils'; -import JSZip from 'jszip'; - -const IGNORED_EXTENSIONS = new Set([ - '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.svg', '.ico', '.webp', - '.mp3', '.wav', '.ogg', '.mp4', '.mov', '.avi', '.webm', - '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', - '.zip', '.rar', '.7z', '.tar', '.gz', - '.exe', '.dll', '.so', '.o', '.a', '.obj', - '.class', '.jar', '.pyc', '.pyd', - '.ds_store', - '.eot', '.ttf', '.woff', '.woff2', -]); - -const IGNORED_DIRS = new Set(['.git', 'node_modules', '__pycache__', '.vscode', '.idea', 'dist', 'build', 'out', 'target', 'coverage', '.next', '.nuxt']); - -interface FileNode { - name: string; - children: FileNode[]; - isDirectory: boolean; -} - -interface ProcessingEntry { - path: string; - // Helper to retrieve content only if the file passes filtering - getContent: () => Promise; - size?: number; -} - -// Check if a path should be ignored based on directory or extension -const shouldIgnore = (path: string): boolean => { - const parts = path.split('/').filter(p => p); - if (parts.some(part => IGNORED_DIRS.has(part) || part.startsWith('.'))) return true; - - const extension = `.${path.split('.').pop()?.toLowerCase()}`; - if (IGNORED_EXTENSIONS.has(extension)) return true; - - return false; -}; - -function buildASCIITree(treeData: FileNode[], rootName: string = 'root'): string { - let structure = `${rootName}\n`; - const generateLines = (nodes: FileNode[], prefix: string) => { - // Sort: directories first, then files, alphabetically - nodes.sort((a, b) => { - if (a.isDirectory === b.isDirectory) return a.name.localeCompare(b.name); - return a.isDirectory ? -1 : 1; - }); - - nodes.forEach((node, index) => { - const isLast = index === nodes.length - 1; - const connector = isLast ? '└── ' : '├── '; - structure += `${prefix}${connector}${node.name}\n`; - if (node.isDirectory && node.children.length > 0) { - const newPrefix = prefix + (isLast ? ' ' : '│ '); - generateLines(node.children, newPrefix); - } - }); - }; - generateLines(treeData, ''); - return structure; -} - -/** - * Unified logic to iterate entries, build the tree structure, and read content. - */ -const processFileEntries = async (entries: ProcessingEntry[], rootName: string): Promise => { - const nodeMap = new Map(); - const roots: FileNode[] = []; - const processedFiles: { path: string, content: string }[] = []; - - // Max file size for text extraction (2MB) - const MAX_TEXT_SIZE = 2 * 1024 * 1024; - - for (const entry of entries) { - // 1. Filtering - if (shouldIgnore(entry.path)) continue; - if (entry.size !== undefined && entry.size > MAX_TEXT_SIZE) continue; - - // 2. Tree Building - const parts = entry.path.split('/').filter(p => p); - let parentNode: FileNode | undefined = undefined; - let currentPath = ''; - - for (let i = 0; i < parts.length; i++) { - const part = parts[i]; - currentPath = parts.slice(0, i + 1).join('/'); - - let currentNode = nodeMap.get(currentPath); - - if (!currentNode) { - const isDir = i < parts.length - 1; - currentNode = { - name: part, - children: [], - isDirectory: isDir, - }; - nodeMap.set(currentPath, currentNode); - - if (parentNode) { - parentNode.children.push(currentNode); - } else { - roots.push(currentNode); - } - } - parentNode = currentNode; - } - - // 3. Content Reading (Only for files, i.e., leaf nodes in this iteration context) - try { - const content = await entry.getContent(); - processedFiles.push({ path: entry.path, content }); - } catch (e) { - console.warn(`Failed to read content for ${entry.path}`, e); - } - } - - // 4. Output Generation - // Optimization: If single root folder, promote its name to rootName to avoid "Project/ProjectName/..." redundancy - let effectiveRootName = rootName; - let effectiveRoots = roots; - - if (roots.length === 1 && roots[0].isDirectory) { - effectiveRootName = roots[0].name; - effectiveRoots = roots[0].children; // Start tree from inside the folder - } - - const structureString = buildASCIITree(effectiveRoots, effectiveRootName); - - let output = "File Structure:\n"; - output += structureString; - output += "\n\nFile Contents:\n"; - - // Sort files by path for consistent output - processedFiles.sort((a, b) => a.path.localeCompare(b.path)); - - for (const file of processedFiles) { - output += `\n--- START OF FILE ${file.path} ---\n`; - output += file.content; - if (file.content && !file.content.endsWith('\n')) { - output += '\n'; - } - output += `--- END OF FILE ${file.path} ---\n`; - } - - const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); - return new File([output], `${effectiveRootName}-context-${timestamp}.txt`, { type: 'text/plain' }); -}; - -export const generateFolderContext = async (files: FileList | File[] | { file: File, path: string }[]): Promise => { - const items = Array.isArray(files) ? files : Array.from(files); - - const entries: ProcessingEntry[] = items.map(item => { - let file: File; - let path: string; - - if ('file' in item && 'path' in item && typeof item.path === 'string') { - // Custom object from Drag & Drop hook - file = item.file; - path = item.path; - } else { - // Standard File object - file = item as File; - path = (file as any).webkitRelativePath || file.name; - } - - return { - path, - size: file.size, - getContent: () => fileToString(file) - }; - }); - - return processFileEntries(entries, "Project"); -}; - -export const generateZipContext = async (zipFile: File): Promise => { - const zip = await JSZip.loadAsync(zipFile); - const zipObjects = Object.values(zip.files) as JSZip.JSZipObject[]; - - const entries: ProcessingEntry[] = zipObjects - .filter(obj => !obj.dir) // Filter out directory entries - .map(obj => ({ - path: obj.name, - getContent: async () => { - // Read as Uint8Array first to detect binary content - const data = await obj.async('uint8array'); - - // Heuristic: Check for null bytes (0x00) in the first 8KB to detect binary files - const checkLimit = Math.min(data.length, 8000); - for (let i = 0; i < checkLimit; i++) { - if (data[i] === 0x00) { - return `[Binary content skipped for file: ${obj.name}]`; - } - } - - // If safe, decode as UTF-8 text - return new TextDecoder().decode(data); - } - })); - - let rootName = zipFile.name.replace(/\.zip$/i, ''); - return processFileEntries(entries, rootName); -}; diff --git a/all-model-chat/vite.config.ts b/all-model-chat/vite.config.ts deleted file mode 100644 index 8aec2bde..00000000 --- a/all-model-chat/vite.config.ts +++ /dev/null @@ -1,38 +0,0 @@ - -import path from 'path'; -import { defineConfig, loadEnv } from 'vite'; -import react from '@vitejs/plugin-react'; - -export default defineConfig(({ mode }) => { - const env = loadEnv(mode, '.', ''); - return { - plugins: [react()], - define: { - 'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY) - }, - resolve: { - alias: { - // __dirname is not available in ES modules. - // We'll resolve from the current working directory. - '@': path.resolve('.'), - } - }, - build: { - rollupOptions: { - // Externalize React and ReactDOM to ensure the app uses the same - // instance as react-pdf (which is loaded via CDN/importmap). - // This prevents the "Cannot read properties of null (reading 'useReducer')" error. - external: [ - 'react', - 'react-dom', - 'react-dom/client', - 'react/jsx-runtime', - 'react-pdf', - 'pdfjs-dist', - '@formkit/auto-animate/react', - 'react-virtuoso' - ] - } - } - }; -}); \ No newline at end of file