diff --git a/.gitignore b/.gitignore index a1a197645f..5903563311 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,6 @@ qdrant_storage/ # allow multiple local clones with different workspaces with different colors # to make it easier to work on features in parallel *.code-workspace + +# Manual release guide +MANUAL_RELEASE.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/api/providers/__tests__/constants.spec.ts b/src/api/providers/__tests__/constants.spec.ts index 56ce0d0a93..d300d19dba 100644 --- a/src/api/providers/__tests__/constants.spec.ts +++ b/src/api/providers/__tests__/constants.spec.ts @@ -55,6 +55,6 @@ describe("DEFAULT_HEADERS", () => { it("should have exactly 4 headers", () => { const headerKeys = Object.keys(DEFAULT_HEADERS) expect(headerKeys).toHaveLength(4) - expect(headerKeys).toEqual(["HTTP-Referer", "X-Title", "X-KiloCode-Version", "User-Agent"]) + expect(headerKeys).toEqual(["HTTP-Referer", "X-Title", "X-AxonCode-Version", "User-Agent"]) }) }) diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index 7424bed8ce..6c71efb5d8 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -113,7 +113,7 @@ describe("OpenAiHandler", () => { defaultHeaders: { "HTTP-Referer": "https://matterai.so", "X-Title": "Axon Code", - "X-KiloCode-Version": Package.version, + "X-AxonCode-Version": Package.version, "User-Agent": `Kilo-Code/${Package.version}`, }, timeout: expect.any(Number), diff --git a/src/api/providers/__tests__/openrouter.spec.ts b/src/api/providers/__tests__/openrouter.spec.ts index 59b2de93b9..8a0ae25230 100644 --- a/src/api/providers/__tests__/openrouter.spec.ts +++ b/src/api/providers/__tests__/openrouter.spec.ts @@ -63,7 +63,7 @@ describe("OpenRouterHandler", () => { defaultHeaders: { "HTTP-Referer": "https://matterai.so", "X-Title": "Axon Code", - "X-KiloCode-Version": Package.version, + "X-AxonCode-Version": Package.version, "User-Agent": `Kilo-Code/${Package.version}`, }, }) diff --git a/src/api/providers/__tests__/requesty.spec.ts b/src/api/providers/__tests__/requesty.spec.ts index 78c5b94be9..3694b1c219 100644 --- a/src/api/providers/__tests__/requesty.spec.ts +++ b/src/api/providers/__tests__/requesty.spec.ts @@ -60,7 +60,7 @@ describe("RequestyHandler", () => { defaultHeaders: { "HTTP-Referer": "https://matterai.so", "X-Title": "Axon Code", - "X-KiloCode-Version": Package.version, + "X-AxonCode-Version": Package.version, "User-Agent": `Kilo-Code/${Package.version}`, }, }) @@ -76,7 +76,7 @@ describe("RequestyHandler", () => { defaultHeaders: { "HTTP-Referer": "https://matterai.so", "X-Title": "Axon Code", - "X-KiloCode-Version": Package.version, + "X-AxonCode-Version": Package.version, "User-Agent": `Kilo-Code/${Package.version}`, }, }) diff --git a/src/api/providers/fetchers/openrouter.ts b/src/api/providers/fetchers/openrouter.ts index 71edc1fe2f..b75ca21238 100644 --- a/src/api/providers/fetchers/openrouter.ts +++ b/src/api/providers/fetchers/openrouter.ts @@ -164,6 +164,7 @@ export async function getOpenRouterModelEndpoints( const baseURL = "https://api.matterai.so/v1/web" try { + console.log("getOpenRouterModelEndpoints 1", baseURL, modelId) const response = await axios.get(`${baseURL}/models/${modelId}`, { timeout: 120000, // 60 seconds timeout }) diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 7f66b093ac..0d9bf255eb 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -146,8 +146,8 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH const baseURL = this.options.openRouterBaseUrl || "https://api.matterai.so/v1/web" const apiKey = this.options.openRouterApiKey ?? "not-provided" + // this.client = new OpenAI({ baseURL, apiKey, defaultHeaders: DEFAULT_HEADERS }) this.client = new OpenAI({ baseURL, apiKey, defaultHeaders: DEFAULT_HEADERS }) - // this.client = new OpenAI({ baseURL: "http://localhost:4064/v1/web", apiKey, defaultHeaders: DEFAULT_HEADERS }) } // kilocode_change start @@ -306,7 +306,6 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH newText = newText.replace(/<\/?think>/g, "") newText = newText.replace(//g, "") - newText = newText.trim() yield { type: "reasoning", diff --git a/src/core/assistant-message/AssistantMessageParser.ts b/src/core/assistant-message/AssistantMessageParser.ts index b38191cee9..1160a1b45e 100644 --- a/src/core/assistant-message/AssistantMessageParser.ts +++ b/src/core/assistant-message/AssistantMessageParser.ts @@ -161,7 +161,14 @@ export class AssistantMessageParser { try { if (accumulatedCall.function!.arguments.trim()) { - parsedArgs = JSON.parse(accumulatedCall.function!.arguments) + // Fix common JSON formatting issues before parsing + let fixedArgs = accumulatedCall.function!.arguments + + // Fix unquoted string values in JSON (e.g., file_pattern:*.js -> file_pattern:"*.js") + // This regex looks for property names followed by colon and unquoted values that contain word characters, dots, asterisks, etc. + fixedArgs = fixedArgs.replace(/("([^"]+)"\s*:\s*)([a-zA-Z0-9_.*\/\\-]+)(?=\s*[,\]}])/g, '$1"$3"') + + parsedArgs = JSON.parse(fixedArgs) // Fix any double-encoded parameters parsedArgs = parseDoubleEncodedParams(parsedArgs) diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index d26e135f47..8c126b41d0 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -154,6 +154,143 @@ The tool accepts these parameters: - \`command\` (required): The CLI command to execute. Must be valid for the user's operating system. - \`cwd\` (optional): The working directory to execute the command in. If not provided, the current working directory is used. Ensure this is always an absolute path, starting with \`/\`. If you are running the command in the root directly, skip this parameter. The command executor is defaulted to run in the root directory. You already have the Current Workspace Directory in . + +## search_files + +The \`search_files\` tool allows you to search for patterns across files in a directory using regex. + +### Parameters + +1. **path** (string, required): Directory to search recursively, relative to workspace +2. **regex** (string, required): Rust-compatible regular expression pattern +3. **file_pattern** (string or null, required): Glob pattern to filter files OR null + +### CRITICAL: file_pattern Must Be a String or null + +**The \`file_pattern\` parameter MUST ALWAYS be:** +- A properly quoted string: \`"*.js"\`, \`"*.tsx"\`, \`"**/*.json"\` +- OR explicitly \`null\` if you want to search all files + +**NEVER provide an unquoted value like \`*.js\` - this will cause a JSON parsing error.** + +### Correct Examples +\`\`\`json +// Search for "import" in all TypeScript files +{ + "path": "src", + "regex": "import.*from", + "file_pattern": "*.ts" +} + +// Search for "TODO" in all files (no filter) +{ + "path": "src", + "regex": "TODO:", + "file_pattern": null +} + +// Search in JSX/TSX files only +{ + "path": "src/components", + "regex": "useState", + "file_pattern": "*.{jsx,tsx}" +} + +// Search in nested directories +{ + "path": ".", + "regex": "API_KEY", + "file_pattern": "**/*.env*" +} +\`\`\` + +### ❌ INCORRECT Examples +\`\`\`json +// WRONG - Unquoted file_pattern (will cause JSON error) +{ + "path": "src", + "regex": "import", + "file_pattern": *.js +} + +// WRONG - Missing file_pattern entirely +{ + "path": "src", + "regex": "import" +} + +// WRONG - Empty string instead of null +{ + "path": "src", + "regex": "import", + "file_pattern": "" +} +\`\`\` + +### Regex Pattern Tips + +- Use Rust regex syntax (similar to PCRE) +- Escape special characters: \`\.\`, \`\(\`, \`\[\`, etc. +- Common patterns: + - \`"word"\` - literal match + - \`"\\bword\\b"\` - word boundary match + - \`"function\\s+\\w+"\` - function declarations + - \`"import.*from\\s+['\"].*['\"]"\` - import statements + +### File Pattern Glob Syntax + +When using a string value for \`file_pattern\`: +- \`"*.js"\` - All .js files in directory +- \`"*.{js,ts}"\` - All .js and .ts files +- \`"**/*.json"\` - All .json files recursively +- \`"test_*.py"\` - Files starting with test_ +- \`"src/**/*.tsx"\` - All .tsx files under src/ + +**When in doubt, use \`null\` to search all files.** + +### Common Use Cases +\`\`\`json +// Find all TODO comments +{ + "path": "src", + "regex": "TODO:|FIXME:", + "file_pattern": null +} + +// Find specific function calls +{ + "path": "src", + "regex": "localStorage\\.(get|set)Item", + "file_pattern": "*.{js,jsx,ts,tsx}" +} + +// Find imports of a specific module +{ + "path": ".", + "regex": "from ['\"]react['\"]", + "file_pattern": "**/*.tsx" +} + +// Find environment variable usage +{ + "path": "src", + "regex": "process\\.env\\.", + "file_pattern": "*.js" +} +\`\`\` + +### Parameter Validation Checklist + +Before submitting, verify: +- ✅ \`path\` is a string (directory path) +- ✅ \`regex\` is a string (valid Rust regex) +- ✅ \`file_pattern\` is EITHER a quoted string OR null +- ✅ All three parameters are present +- ✅ No unquoted glob patterns like \`*.js\` + +### Remember + +**Always quote the file_pattern value or use null. Never use bare/unquoted glob patterns.** ` async function generatePrompt( diff --git a/src/package.json b/src/package.json index 3dff80b08d..e06a649d74 100644 --- a/src/package.json +++ b/src/package.json @@ -3,7 +3,7 @@ "displayName": "%extension.displayName%", "description": "%extension.description%", "publisher": "matterai", - "version": "4.115.0", + "version": "4.116.0", "icon": "assets/icons/matterai-ic.png", "galleryBanner": { "color": "#FFFFFF", diff --git a/src/services/code-index/config-manager.ts b/src/services/code-index/config-manager.ts index 2c0e8bb5c9..641f9a046d 100644 --- a/src/services/code-index/config-manager.ts +++ b/src/services/code-index/config-manager.ts @@ -20,10 +20,12 @@ export class CodeIndexConfigManager { private geminiOptions?: { apiKey: string } private mistralOptions?: { apiKey: string } private vercelAiGatewayOptions?: { apiKey: string } + private openRouterApiKey?: string private qdrantUrl?: string = "http://localhost:6333" private qdrantApiKey?: string private searchMinScore?: number private searchMaxResults?: number + private matterAiOptions?: { apiKey: string } constructor(private readonly contextProxy: ContextProxy) { // Initialize with current configuration to avoid false restart triggers @@ -65,6 +67,8 @@ export class CodeIndexConfigManager { const openAiKey = this.contextProxy?.getSecret("codeIndexOpenAiKey") ?? "" const qdrantApiKey = this.contextProxy?.getSecret("codeIndexQdrantApiKey") ?? "" + // Get openRouterApiKey from main API configuration, not as separate secret + const openRouterApiKey = this.contextProxy?.getSecret("openRouterApiKey") ?? "" // Fix: Read OpenAI Compatible settings from the correct location within codebaseIndexConfig const openAiCompatibleBaseUrl = codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl ?? "" const openAiCompatibleApiKey = this.contextProxy?.getSecret("codebaseIndexOpenAiCompatibleApiKey") ?? "" @@ -76,6 +80,7 @@ export class CodeIndexConfigManager { this.codebaseIndexEnabled = codebaseIndexEnabled ?? true this.qdrantUrl = codebaseIndexQdrantUrl this.qdrantApiKey = qdrantApiKey ?? "" + this.openRouterApiKey = openRouterApiKey this.searchMinScore = codebaseIndexSearchMinScore this.searchMaxResults = codebaseIndexSearchMaxResults @@ -395,10 +400,12 @@ export class CodeIndexConfigManager { geminiOptions: this.geminiOptions, mistralOptions: this.mistralOptions, vercelAiGatewayOptions: this.vercelAiGatewayOptions, + openRouterApiKey: this.openRouterApiKey, qdrantUrl: this.qdrantUrl, qdrantApiKey: this.qdrantApiKey, - searchMinScore: this.currentSearchMinScore, - searchMaxResults: this.currentSearchMaxResults, + searchMinScore: this.searchMinScore, + searchMaxResults: this.searchMaxResults, + matterAiOptions: this.matterAiOptions, } } diff --git a/src/services/code-index/embedders/matterai.ts b/src/services/code-index/embedders/matterai.ts new file mode 100644 index 0000000000..b0afb71574 --- /dev/null +++ b/src/services/code-index/embedders/matterai.ts @@ -0,0 +1,88 @@ +import { OpenAICompatibleEmbedder } from "./openai-compatible" +import { IEmbedder, EmbeddingResponse, EmbedderInfo } from "../interfaces/embedder" +import { GEMINI_MAX_ITEM_TOKENS, MAX_ITEM_TOKENS } from "../constants" +import { t } from "../../../i18n" +import { TelemetryEventName } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" + +/** + * MatterAI embedder implementation that wraps the OpenAI Compatible embedder + * with configuration for MatterAI's embedding API. + * + * Supported models: + * - matterai-embedding-large (dimension: 3072) + */ +export class MatterAiEmbedder implements IEmbedder { + private readonly openAICompatibleEmbedder: OpenAICompatibleEmbedder + private static readonly MATTERAI_BASE_URL = "https://api.matterai.so/v1/embed" + private static readonly DEFAULT_MODEL = "matterai-embedding-large" + private readonly modelId: string + + /** + * Creates a new MatterAI embedder + * @param modelId The model ID to use (defaults to matterai-embedding-large) + */ + constructor(apiKey: string, modelId?: string) { + // Use provided model or default + this.modelId = modelId || MatterAiEmbedder.DEFAULT_MODEL + + // Create an OpenAI Compatible embedder with MatterAI's configuration + this.openAICompatibleEmbedder = new OpenAICompatibleEmbedder( + MatterAiEmbedder.MATTERAI_BASE_URL, + apiKey, + this.modelId, + 2048, + ) + } + + /** + * Creates embeddings for the given texts using Gemini's embedding API + * @param texts Array of text strings to embed + * @param model Optional model identifier (uses constructor model if not provided) + * @returns Promise resolving to embedding response + */ + async createEmbeddings(texts: string[], model?: string): Promise { + try { + // Use the provided model or fall back to the instance's model + const modelToUse = model || this.modelId + return await this.openAICompatibleEmbedder.createEmbeddings(texts, modelToUse) + } catch (error) { + TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + location: "MatterAiEmbedder:createEmbeddings", + }) + console.error("MatterAI embedder error in createEmbeddings:", error) // kilocode_change + throw error + } + } + + /** + * Validates the MatterAI embedder configuration by delegating to the underlying OpenAI-compatible embedder + * @returns Promise resolving to validation result with success status and optional error message + */ + async validateConfiguration(): Promise<{ valid: boolean; error?: string }> { + try { + // Delegate validation to the OpenAI-compatible embedder + // The error messages will be specific to MatterAI since we're using MatterAI's base URL + return await this.openAICompatibleEmbedder.validateConfiguration() + } catch (error) { + TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + location: "MatterAiEmbedder:validateConfiguration", + }) + console.error("MatterAI embedder error in validateConfiguration:", error) // kilocode_change + throw error + } + } + + /** + * Returns information about this embedder + */ + get embedderInfo(): EmbedderInfo { + return { + name: "matterai", + } + } +} diff --git a/src/services/code-index/interfaces/config.ts b/src/services/code-index/interfaces/config.ts index f168e26869..602e91b8b1 100644 --- a/src/services/code-index/interfaces/config.ts +++ b/src/services/code-index/interfaces/config.ts @@ -15,10 +15,12 @@ export interface CodeIndexConfig { geminiOptions?: { apiKey: string } mistralOptions?: { apiKey: string } vercelAiGatewayOptions?: { apiKey: string } + openRouterApiKey?: string // Add OpenRouter API key field qdrantUrl?: string qdrantApiKey?: string searchMinScore?: number searchMaxResults?: number + matterAiOptions?: { apiKey: string } } /** diff --git a/src/services/code-index/interfaces/embedder.ts b/src/services/code-index/interfaces/embedder.ts index 1fcda3aca3..ddcdb8be94 100644 --- a/src/services/code-index/interfaces/embedder.ts +++ b/src/services/code-index/interfaces/embedder.ts @@ -28,7 +28,14 @@ export interface EmbeddingResponse { } } -export type AvailableEmbedders = "openai" | "ollama" | "openai-compatible" | "gemini" | "mistral" | "vercel-ai-gateway" +export type AvailableEmbedders = + | "openai" + | "ollama" + | "openai-compatible" + | "gemini" + | "mistral" + | "vercel-ai-gateway" + | "matterai" export interface EmbedderInfo { name: AvailableEmbedders diff --git a/src/services/code-index/interfaces/manager.ts b/src/services/code-index/interfaces/manager.ts index 527900f6d1..af3d937d6f 100644 --- a/src/services/code-index/interfaces/manager.ts +++ b/src/services/code-index/interfaces/manager.ts @@ -70,7 +70,14 @@ export interface ICodeIndexManager { } export type IndexingState = "Standby" | "Indexing" | "Indexed" | "Error" -export type EmbedderProvider = "openai" | "ollama" | "openai-compatible" | "gemini" | "mistral" | "vercel-ai-gateway" +export type EmbedderProvider = + | "openai" + | "ollama" + | "openai-compatible" + | "gemini" + | "mistral" + | "vercel-ai-gateway" + | "matterai" export interface IndexProgressUpdate { systemStatus: IndexingState diff --git a/src/services/code-index/service-factory.ts b/src/services/code-index/service-factory.ts index 6d69e1f0b6..747070e516 100644 --- a/src/services/code-index/service-factory.ts +++ b/src/services/code-index/service-factory.ts @@ -18,6 +18,7 @@ import { TelemetryService } from "@roo-code/telemetry" import { TelemetryEventName } from "@roo-code/types" import { Package } from "../../shared/package" import { BATCH_SEGMENT_THRESHOLD } from "./constants" +import { MatterAiEmbedder } from "./embedders/matterai" /** * Factory class responsible for creating and configuring code indexing service dependencies. @@ -36,51 +37,14 @@ export class CodeIndexServiceFactory { const config = this.configManager.getConfig() const provider = config.embedderProvider as EmbedderProvider - - if (provider === "openai") { - const apiKey = config.openAiOptions?.openAiNativeApiKey - - if (!apiKey) { - throw new Error(t("embeddings:serviceFactory.openAiConfigMissing")) - } - return new OpenAiEmbedder({ - ...config.openAiOptions, - openAiEmbeddingModelId: config.modelId, - }) - } else if (provider === "ollama") { - if (!config.ollamaOptions?.ollamaBaseUrl) { - throw new Error(t("embeddings:serviceFactory.ollamaConfigMissing")) - } - return new CodeIndexOllamaEmbedder({ - ...config.ollamaOptions, - ollamaModelId: config.modelId, - }) - } else if (provider === "openai-compatible") { - if (!config.openAiCompatibleOptions?.baseUrl || !config.openAiCompatibleOptions?.apiKey) { - throw new Error(t("embeddings:serviceFactory.openAiCompatibleConfigMissing")) - } - return new OpenAICompatibleEmbedder( - config.openAiCompatibleOptions.baseUrl, - config.openAiCompatibleOptions.apiKey, - config.modelId, - ) - } else if (provider === "gemini") { - if (!config.geminiOptions?.apiKey) { - throw new Error(t("embeddings:serviceFactory.geminiConfigMissing")) - } - return new GeminiEmbedder(config.geminiOptions.apiKey, config.modelId) - } else if (provider === "mistral") { - if (!config.mistralOptions?.apiKey) { - throw new Error(t("embeddings:serviceFactory.mistralConfigMissing")) + if (provider === "matterai") { + // Use the same API key source as OpenRouter provider + const openRouterApiKey = config.openRouterApiKey + if (!openRouterApiKey) { + throw new Error(t("embeddings:serviceFactory.matteraiConfigMissing")) } - return new MistralEmbedder(config.mistralOptions.apiKey, config.modelId) - } else if (provider === "vercel-ai-gateway") { - if (!config.vercelAiGatewayOptions?.apiKey) { - throw new Error(t("embeddings:serviceFactory.vercelAiGatewayConfigMissing")) - } - return new VercelAiGatewayEmbedder(config.vercelAiGatewayOptions.apiKey, config.modelId) + return new MatterAiEmbedder(openRouterApiKey, config.modelId) } - throw new Error( t("embeddings:serviceFactory.invalidEmbedderType", { embedderProvider: config.embedderProvider }), ) diff --git a/src/shared/embeddingModels.ts b/src/shared/embeddingModels.ts index 80c51a6b45..00de9f7259 100644 --- a/src/shared/embeddingModels.ts +++ b/src/shared/embeddingModels.ts @@ -2,7 +2,14 @@ * Defines profiles for different embedding models, including their dimensions. */ -export type EmbedderProvider = "openai" | "ollama" | "openai-compatible" | "gemini" | "mistral" | "vercel-ai-gateway" // Add other providers as needed +export type EmbedderProvider = + | "openai" + | "ollama" + | "openai-compatible" + | "gemini" + | "mistral" + | "vercel-ai-gateway" + | "matterai" // Add other providers as needed export interface EmbeddingModelProfile { dimension: number @@ -19,6 +26,9 @@ export type EmbeddingModelProfiles = { // Example profiles - expand this list as needed export const EMBEDDING_MODEL_PROFILES: EmbeddingModelProfiles = { + matterai: { + "matterai-embedding-large": { dimension: 3072, scoreThreshold: 0.4 }, + }, openai: { "text-embedding-3-small": { dimension: 1536, scoreThreshold: 0.4 }, "text-embedding-3-large": { dimension: 3072, scoreThreshold: 0.4 }, diff --git a/src/shared/kilocode/headers.ts b/src/shared/kilocode/headers.ts index 9f7be47c0e..595b4004b8 100644 --- a/src/shared/kilocode/headers.ts +++ b/src/shared/kilocode/headers.ts @@ -1,5 +1,5 @@ -export const X_KILOCODE_VERSION = "X-KiloCode-Version" +export const X_KILOCODE_VERSION = "X-AxonCode-Version" export const X_KILOCODE_ORGANIZATIONID = "X-KiloCode-OrganizationId" -export const X_KILOCODE_TASKID = "X-KiloCode-TaskId" +export const X_KILOCODE_TASKID = "X-AxonCode-TaskId" export const X_KILOCODE_PROJECTID = "X-KiloCode-ProjectId" export const X_KILOCODE_TESTER = "X-KILOCODE-TESTER" diff --git a/src/shared/package.ts b/src/shared/package.ts index b071de8278..2e4a4829e1 100644 --- a/src/shared/package.ts +++ b/src/shared/package.ts @@ -10,6 +10,6 @@ export const Package = { publisher, name: process.env.PKG_NAME || name, version: process.env.PKG_VERSION || version, - outputChannel: process.env.PKG_OUTPUT_CHANNEL || "Kilo-Code", + outputChannel: process.env.PKG_OUTPUT_CHANNEL || "AxonCode", sha: process.env.PKG_SHA, } as const diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 50587b3233..2dc0b72091 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -52,6 +52,7 @@ import { ListTree, MessageCircleQuestionMark, PocketKnife, + Search, TerminalSquare, Trash2, } from "lucide-react" @@ -779,7 +780,7 @@ export const ChatRowContent = ({ return ( <>
- {toolIcon("search")} + {message.type === "ask" ? ( {t("chat:text.rooSaid")}
*/} -
+
{message.images && message.images.length > 0 && (
diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index e3984a458e..d35a0ddf6f 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -1301,7 +1301,7 @@ export const ChatTextArea = forwardRef( "z-[2]", "scrollbar-none", "scrollbar-hide", - "p-2", + "pb-14", )} onScroll={() => updateHighlights()} /> diff --git a/webview-ui/src/components/chat/CheckpointRestoreDialog.tsx b/webview-ui/src/components/chat/CheckpointRestoreDialog.tsx index b862d250be..293dd315e2 100644 --- a/webview-ui/src/components/chat/CheckpointRestoreDialog.tsx +++ b/webview-ui/src/components/chat/CheckpointRestoreDialog.tsx @@ -51,7 +51,7 @@ export const CheckpointRestoreDialog: React.FC = ( {title} {description} - + {t("common:answers.cancel")} diff --git a/webview-ui/src/components/chat/CodeIndexPopover.tsx b/webview-ui/src/components/chat/CodeIndexPopover.tsx index 62164b1460..b330606959 100644 --- a/webview-ui/src/components/chat/CodeIndexPopover.tsx +++ b/webview-ui/src/components/chat/CodeIndexPopover.tsx @@ -1,33 +1,16 @@ -import React, { useState, useEffect, useMemo, useCallback, useRef } from "react" -import { Trans } from "react-i18next" -import { z } from "zod" -import { - VSCodeButton, - VSCodeTextField, - VSCodeDropdown, - VSCodeOption, - VSCodeLink, - VSCodeCheckbox, -} from "@vscode/webview-ui-toolkit/react" import * as ProgressPrimitive from "@radix-ui/react-progress" +import { VSCodeButton, VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react" import { AlertTriangle } from "lucide-react" +import React, { useCallback, useEffect, useMemo, useRef, useState } from "react" +import { Trans } from "react-i18next" +import { z } from "zod" import { CODEBASE_INDEX_DEFAULTS } from "@roo-code/types" import type { EmbedderProvider } from "@roo/embeddingModels" import type { IndexingStatus } from "@roo/ExtensionMessage" -import { vscode } from "@src/utils/vscode" -import { useExtensionState } from "@src/context/ExtensionStateContext" -import { useAppTranslation } from "@src/i18n/TranslationContext" -import { buildDocLink } from "@src/utils/docLinks" -import { cn } from "@src/lib/utils" import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, AlertDialog, AlertDialogAction, AlertDialogCancel, @@ -39,15 +22,19 @@ import { AlertDialogTrigger, Popover, PopoverContent, - Slider, StandardTooltip, } from "@src/components/ui" import { useRooPortal } from "@src/components/ui/hooks/useRooPortal" +import { useExtensionState } from "@src/context/ExtensionStateContext" import { useEscapeKey } from "@src/hooks/useEscapeKey" +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { cn } from "@src/lib/utils" +import { buildDocLink } from "@src/utils/docLinks" +import { vscode } from "@src/utils/vscode" // Default URLs for providers -const DEFAULT_QDRANT_URL = "http://localhost:6333" -const DEFAULT_OLLAMA_URL = "http://localhost:11434" +// const DEFAULT_QDRANT_URL = "http://localhost:6333" +// const DEFAULT_OLLAMA_URL = "http://localhost:11434" interface CodeIndexPopoverProps { children: React.ReactNode @@ -160,10 +147,10 @@ export const CodeIndexPopover: React.FC = ({ }) => { const SECRET_PLACEHOLDER = "••••••••••••••••" const { t } = useAppTranslation() - const { codebaseIndexConfig, codebaseIndexModels, cwd } = useExtensionState() + const { codebaseIndexConfig, cwd } = useExtensionState() const [open, setOpen] = useState(false) - const [isAdvancedSettingsOpen, setIsAdvancedSettingsOpen] = useState(false) - const [isSetupSettingsOpen, setIsSetupSettingsOpen] = useState(false) + // const [isAdvancedSettingsOpen, setIsAdvancedSettingsOpen] = useState(false) + // const [isSetupSettingsOpen, setIsSetupSettingsOpen] = useState(false) const [indexingStatus, setIndexingStatus] = useState(externalIndexingStatus) @@ -539,12 +526,12 @@ export const CodeIndexPopover: React.FC = ({ const transformStyleString = `translateX(-${100 - progressPercentage}%)` - const getAvailableModels = () => { - if (!codebaseIndexModels) return [] + // const getAvailableModels = () => { + // if (!codebaseIndexModels) return [] - const models = codebaseIndexModels[currentSettings.codebaseIndexEmbedderProvider] - return models ? Object.keys(models) : [] - } + // const models = codebaseIndexModels[currentSettings.codebaseIndexEmbedderProvider] + // return models ? Object.keys(models) : [] + // } const portalContainer = useRooPortal("roo-portal") @@ -631,677 +618,6 @@ export const CodeIndexPopover: React.FC = ({ )}
- {/* Setup Settings Disclosure */} -
- - - {isSetupSettingsOpen && ( -
- {/* Embedder Provider Section */} -
- - -
- - {/* Provider-specific settings */} - {currentSettings.codebaseIndexEmbedderProvider === "openai" && ( - <> -
- - - updateSetting("codeIndexOpenAiKey", e.target.value) - } - placeholder={t("settings:codeIndex.openAiKeyPlaceholder")} - className={cn("w-full", { - "border-red-500": formErrors.codeIndexOpenAiKey, - })} - /> - {formErrors.codeIndexOpenAiKey && ( -

- {formErrors.codeIndexOpenAiKey} -

- )} -
- -
- - - updateSetting("codebaseIndexEmbedderModelId", e.target.value) - } - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexEmbedderModelId, - })}> - - {t("settings:codeIndex.selectModel")} - - {getAvailableModels().map((modelId) => { - const model = - codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider - ]?.[modelId] - return ( - - {modelId}{" "} - {model - ? t("settings:codeIndex.modelDimensions", { - dimension: model.dimension, - }) - : ""} - - ) - })} - - {formErrors.codebaseIndexEmbedderModelId && ( -

- {formErrors.codebaseIndexEmbedderModelId} -

- )} -
- - )} - - {currentSettings.codebaseIndexEmbedderProvider === "ollama" && ( - <> -
- - - updateSetting("codebaseIndexEmbedderBaseUrl", e.target.value) - } - onBlur={(e: any) => { - // Set default Ollama URL if field is empty - if (!e.target.value.trim()) { - e.target.value = DEFAULT_OLLAMA_URL - updateSetting( - "codebaseIndexEmbedderBaseUrl", - DEFAULT_OLLAMA_URL, - ) - } - }} - placeholder={t("settings:codeIndex.ollamaUrlPlaceholder")} - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexEmbedderBaseUrl, - })} - /> - {formErrors.codebaseIndexEmbedderBaseUrl && ( -

- {formErrors.codebaseIndexEmbedderBaseUrl} -

- )} -
- -
- - - updateSetting("codebaseIndexEmbedderModelId", e.target.value) - } - placeholder={t("settings:codeIndex.modelPlaceholder")} - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexEmbedderModelId, - })} - /> - {formErrors.codebaseIndexEmbedderModelId && ( -

- {formErrors.codebaseIndexEmbedderModelId} -

- )} -
- -
- - { - const value = e.target.value - ? parseInt(e.target.value, 10) || undefined - : undefined - updateSetting("codebaseIndexEmbedderModelDimension", value) - }} - placeholder={t("settings:codeIndex.modelDimensionPlaceholder")} - className={cn("w-full", { - "border-red-500": - formErrors.codebaseIndexEmbedderModelDimension, - })} - /> - {formErrors.codebaseIndexEmbedderModelDimension && ( -

- {formErrors.codebaseIndexEmbedderModelDimension} -

- )} -
- - )} - - {currentSettings.codebaseIndexEmbedderProvider === "openai-compatible" && ( - <> -
- - - updateSetting( - "codebaseIndexOpenAiCompatibleBaseUrl", - e.target.value, - ) - } - placeholder={t( - "settings:codeIndex.openAiCompatibleBaseUrlPlaceholder", - )} - className={cn("w-full", { - "border-red-500": - formErrors.codebaseIndexOpenAiCompatibleBaseUrl, - })} - /> - {formErrors.codebaseIndexOpenAiCompatibleBaseUrl && ( -

- {formErrors.codebaseIndexOpenAiCompatibleBaseUrl} -

- )} -
- -
- - - updateSetting( - "codebaseIndexOpenAiCompatibleApiKey", - e.target.value, - ) - } - placeholder={t( - "settings:codeIndex.openAiCompatibleApiKeyPlaceholder", - )} - className={cn("w-full", { - "border-red-500": - formErrors.codebaseIndexOpenAiCompatibleApiKey, - })} - /> - {formErrors.codebaseIndexOpenAiCompatibleApiKey && ( -

- {formErrors.codebaseIndexOpenAiCompatibleApiKey} -

- )} -
- -
- - - updateSetting("codebaseIndexEmbedderModelId", e.target.value) - } - placeholder={t("settings:codeIndex.modelPlaceholder")} - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexEmbedderModelId, - })} - /> - {formErrors.codebaseIndexEmbedderModelId && ( -

- {formErrors.codebaseIndexEmbedderModelId} -

- )} -
- -
- - { - const value = e.target.value - ? parseInt(e.target.value, 10) || undefined - : undefined - updateSetting("codebaseIndexEmbedderModelDimension", value) - }} - placeholder={t("settings:codeIndex.modelDimensionPlaceholder")} - className={cn("w-full", { - "border-red-500": - formErrors.codebaseIndexEmbedderModelDimension, - })} - /> - {formErrors.codebaseIndexEmbedderModelDimension && ( -

- {formErrors.codebaseIndexEmbedderModelDimension} -

- )} -
- - )} - - {currentSettings.codebaseIndexEmbedderProvider === "gemini" && ( - <> -
- - - updateSetting("codebaseIndexGeminiApiKey", e.target.value) - } - placeholder={t("settings:codeIndex.geminiApiKeyPlaceholder")} - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexGeminiApiKey, - })} - /> - {formErrors.codebaseIndexGeminiApiKey && ( -

- {formErrors.codebaseIndexGeminiApiKey} -

- )} -
- -
- - - updateSetting("codebaseIndexEmbedderModelId", e.target.value) - } - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexEmbedderModelId, - })}> - - {t("settings:codeIndex.selectModel")} - - {getAvailableModels().map((modelId) => { - const model = - codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider - ]?.[modelId] - return ( - - {modelId}{" "} - {model - ? t("settings:codeIndex.modelDimensions", { - dimension: model.dimension, - }) - : ""} - - ) - })} - - {formErrors.codebaseIndexEmbedderModelId && ( -

- {formErrors.codebaseIndexEmbedderModelId} -

- )} -
- - )} - - {currentSettings.codebaseIndexEmbedderProvider === "mistral" && ( - <> -
- - - updateSetting("codebaseIndexMistralApiKey", e.target.value) - } - placeholder={t("settings:codeIndex.mistralApiKeyPlaceholder")} - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexMistralApiKey, - })} - /> - {formErrors.codebaseIndexMistralApiKey && ( -

- {formErrors.codebaseIndexMistralApiKey} -

- )} -
- -
- - - updateSetting("codebaseIndexEmbedderModelId", e.target.value) - } - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexEmbedderModelId, - })}> - - {t("settings:codeIndex.selectModel")} - - {getAvailableModels().map((modelId) => { - const model = - codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider - ]?.[modelId] - return ( - - {modelId}{" "} - {model - ? t("settings:codeIndex.modelDimensions", { - dimension: model.dimension, - }) - : ""} - - ) - })} - - {formErrors.codebaseIndexEmbedderModelId && ( -

- {formErrors.codebaseIndexEmbedderModelId} -

- )} -
- - )} - - {currentSettings.codebaseIndexEmbedderProvider === "vercel-ai-gateway" && ( - <> -
- - - updateSetting( - "codebaseIndexVercelAiGatewayApiKey", - e.target.value, - ) - } - placeholder={t( - "settings:codeIndex.vercelAiGatewayApiKeyPlaceholder", - )} - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexVercelAiGatewayApiKey, - })} - /> - {formErrors.codebaseIndexVercelAiGatewayApiKey && ( -

- {formErrors.codebaseIndexVercelAiGatewayApiKey} -

- )} -
- -
- - - updateSetting("codebaseIndexEmbedderModelId", e.target.value) - } - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexEmbedderModelId, - })}> - - {t("settings:codeIndex.selectModel")} - - {getAvailableModels().map((modelId) => { - const model = - codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider - ]?.[modelId] - return ( - - {modelId}{" "} - {model - ? t("settings:codeIndex.modelDimensions", { - dimension: model.dimension, - }) - : ""} - - ) - })} - - {formErrors.codebaseIndexEmbedderModelId && ( -

- {formErrors.codebaseIndexEmbedderModelId} -

- )} -
- - )} - - {/* Qdrant Settings */} -
- - - updateSetting("codebaseIndexQdrantUrl", e.target.value) - } - onBlur={(e: any) => { - // Set default Qdrant URL if field is empty - if (!e.target.value.trim()) { - currentSettings.codebaseIndexQdrantUrl = DEFAULT_QDRANT_URL - updateSetting("codebaseIndexQdrantUrl", DEFAULT_QDRANT_URL) - } - }} - placeholder={t("settings:codeIndex.qdrantUrlPlaceholder")} - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexQdrantUrl, - })} - /> - {formErrors.codebaseIndexQdrantUrl && ( -

- {formErrors.codebaseIndexQdrantUrl} -

- )} -
- -
- - updateSetting("codeIndexQdrantApiKey", e.target.value)} - placeholder={t("settings:codeIndex.qdrantApiKeyPlaceholder")} - className={cn("w-full", { - "border-red-500": formErrors.codeIndexQdrantApiKey, - })} - /> - {formErrors.codeIndexQdrantApiKey && ( -

- {formErrors.codeIndexQdrantApiKey} -

- )} -
-
- )} -
- - {/* Advanced Settings Disclosure */} -
- - - {isAdvancedSettingsOpen && ( -
- {/* Search Score Threshold Slider */} -
-
- - - - -
-
- - updateSetting("codebaseIndexSearchMinScore", values[0]) - } - className="flex-1" - data-testid="search-min-score-slider" - /> - - {( - currentSettings.codebaseIndexSearchMinScore ?? - CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_MIN_SCORE - ).toFixed(2)} - - - updateSetting( - "codebaseIndexSearchMinScore", - CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_MIN_SCORE, - ) - }> - - -
-
- - {/* Maximum Search Results Slider */} -
-
- - - - -
-
- - updateSetting("codebaseIndexSearchMaxResults", values[0]) - } - className="flex-1" - data-testid="search-max-results-slider" - /> - - {currentSettings.codebaseIndexSearchMaxResults ?? - CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_RESULTS} - - - updateSetting( - "codebaseIndexSearchMaxResults", - CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_RESULTS, - ) - }> - - -
-
-
- )} -
- {/* Action Buttons */}
diff --git a/webview-ui/src/components/chat/checkpoints/CheckpointSaved.tsx b/webview-ui/src/components/chat/checkpoints/CheckpointSaved.tsx index dca22db1b4..ff4a2ff5bb 100644 --- a/webview-ui/src/components/chat/checkpoints/CheckpointSaved.tsx +++ b/webview-ui/src/components/chat/checkpoints/CheckpointSaved.tsx @@ -1,10 +1,10 @@ -import { useMemo, useRef, useState, useEffect } from "react" -import { useTranslation } from "react-i18next" import { cn } from "@/lib/utils" +import { useEffect, useMemo, useRef, useState } from "react" +import { useTranslation } from "react-i18next" +import { CornerDownRight } from "lucide-react" import { CheckpointMenu } from "./CheckpointMenu" import { checkpointSchema } from "./schema" -import { GitCommitVertical } from "lucide-react" type CheckpointSavedProps = { ts: number @@ -77,7 +77,7 @@ export const CheckpointSaved = ({ checkpoint, ...props }: CheckpointSavedProps) return (
- + {t("chat:checkpoint.regular")} {isCurrent && ({t("chat:checkpoint.current")})}
diff --git a/webview-ui/src/components/common/CodeBlock.tsx b/webview-ui/src/components/common/CodeBlock.tsx index b13a6ec24d..a1029f576f 100644 --- a/webview-ui/src/components/common/CodeBlock.tsx +++ b/webview-ui/src/components/common/CodeBlock.tsx @@ -98,7 +98,7 @@ const CodeBlockButtonWrapper = styled.div` const CodeBlockContainer = styled.div` position: relative; overflow: hidden; - background-color: ${CODE_BLOCK_BG_COLOR}; + background-color: ${CODE_BLOCK_BG_COLOR} !important; ${CodeBlockButtonWrapper} { opacity: 0; @@ -119,7 +119,7 @@ export const StyledPre = styled.div<{ windowshade?: "true" | "false" collapsedHeight?: number }>` - background-color: ${CODE_BLOCK_BG_COLOR}; + background-color: ${CODE_BLOCK_BG_COLOR} !important; max-height: ${({ windowshade, collapsedHeight }) => windowshade === "true" ? `${collapsedHeight || WINDOW_SHADE_SETTINGS.collapsedHeight}px` : "none"}; overflow-y: auto; @@ -128,7 +128,7 @@ export const StyledPre = styled.div<{ ${({ preStyle }) => preStyle && { ...preStyle }} pre { - background-color: ${CODE_BLOCK_BG_COLOR}; + background-color: ${CODE_BLOCK_BG_COLOR} !important; border-radius: 5px; margin: 0; padding: 10px; @@ -160,8 +160,8 @@ export const StyledPre = styled.div<{ } .hljs { - color: var(--vscode-editor-foreground, #fff); - background-color: ${CODE_BLOCK_BG_COLOR}; + color: var(--vscode-editor-foreground, #fff) !important; + background-color: ${CODE_BLOCK_BG_COLOR} !important; } ` diff --git a/webview-ui/src/components/common/MarkdownBlock.tsx b/webview-ui/src/components/common/MarkdownBlock.tsx index b4435e6e3e..c1e51805c9 100644 --- a/webview-ui/src/components/common/MarkdownBlock.tsx +++ b/webview-ui/src/components/common/MarkdownBlock.tsx @@ -44,8 +44,8 @@ const StyledMarkdown = styled.div` font-family: var(--vscode-editor-font-family, monospace); font-size: 0.85em; filter: saturation(110%) brightness(95%); - color: color-mix(in srgb, var(--color-matterai-green) 80%, transparent) !important; - background-color: color-mix(in srgb, var(--color-matterai-green) 10%, transparent) !important; + color: #ffffff !important; + background-color: black !important; padding: 1px 2px; white-space: pre-line; word-break: break-word; diff --git a/webview-ui/src/components/kilocode/common/CodeBlock.tsx b/webview-ui/src/components/kilocode/common/CodeBlock.tsx index 93ef3af905..b12ac5fee4 100644 --- a/webview-ui/src/components/kilocode/common/CodeBlock.tsx +++ b/webview-ui/src/components/kilocode/common/CodeBlock.tsx @@ -1,14 +1,13 @@ -import { memo, useEffect, useRef, useCallback, useState } from "react" -import styled from "styled-components" +import { StandardTooltip } from "@/components/ui" +import { useAppTranslation } from "@src/i18n/TranslationContext" import { useCopyToClipboard } from "@src/utils/clipboard" -import { getHighlighter, isLanguageLoaded, normalizeLanguage, ExtendedLanguage } from "@src/utils/highlighter" -import { bundledLanguages } from "shiki" -import type { ShikiTransformer } from "shiki" +import { ExtendedLanguage, getHighlighter, isLanguageLoaded, normalizeLanguage } from "@src/utils/highlighter" import { toJsxRuntime } from "hast-util-to-jsx-runtime" +import { Check, Copy } from "lucide-react" +import { memo, useCallback, useEffect, useRef, useState } from "react" import { Fragment, jsx, jsxs } from "react/jsx-runtime" -import { ChevronDown, ChevronUp, WrapText, AlignJustify, Copy, Check } from "lucide-react" -import { useAppTranslation } from "@src/i18n/TranslationContext" -import { StandardTooltip } from "@/components/ui" +import type { ShikiTransformer } from "shiki" +import styled from "styled-components" export const CODE_BLOCK_BG_COLOR = "var(--vscode-editor-background, --vscode-sideBar-background, rgb(30 30 30))" export const WRAPPER_ALPHA = "cc" // 80% opacity @@ -124,6 +123,7 @@ export const StyledPre = styled.div<{ pre, code { /* Undefined wordwrap defaults to true (pre-wrap) behavior. */ + background-color: var(--vscode-textCodeBlock-background) !important; white-space: ${({ wordwrap }) => (wordwrap === "false" ? "pre" : "pre-wrap")}; word-break: ${({ wordwrap }) => (wordwrap === "false" ? "normal" : "normal")}; overflow-wrap: ${({ wordwrap }) => (wordwrap === "false" ? "normal" : "break-word")}; @@ -150,51 +150,51 @@ export const StyledPre = styled.div<{ } ` -const LanguageSelect = styled.select` - font-size: 12px; - color: var(--vscode-foreground); - opacity: 0.4; - font-family: monospace; - appearance: none; - background: transparent; - border: none; - cursor: pointer; - padding: 4px; - margin: 0; - vertical-align: middle; - height: 24px; - - & option { - background: var(--vscode-editor-background); - color: var(--vscode-foreground); - padding: 0; - margin: 0; - } - - &::-webkit-scrollbar { - width: 6px; - } - - &::-webkit-scrollbar-thumb { - background: var(--vscode-scrollbarSlider-background); - } - - &::-webkit-scrollbar-track { - background: var(--vscode-editor-background); - } - - &:hover { - opacity: 1; - background: var(--vscode-toolbar-hoverBackground); - border-radius: 3px; - } - - &:focus { - opacity: 1; - outline: none; - border-radius: 3px; - } -` +// const LanguageSelect = styled.select` +// font-size: 12px; +// color: var(--vscode-foreground); +// opacity: 0.4; +// font-family: monospace; +// appearance: none; +// background: transparent; +// border: none; +// cursor: pointer; +// padding: 4px; +// margin: 0; +// vertical-align: middle; +// height: 24px; + +// & option { +// background: var(--vscode-editor-background); +// color: var(--vscode-foreground); +// padding: 0; +// margin: 0; +// } + +// &::-webkit-scrollbar { +// width: 6px; +// } + +// &::-webkit-scrollbar-thumb { +// background: var(--vscode-scrollbarSlider-background); +// } + +// &::-webkit-scrollbar-track { +// background: var(--vscode-editor-background); +// } + +// &:hover { +// opacity: 1; +// background: var(--vscode-toolbar-hoverBackground); +// border-radius: 3px; +// } + +// &:focus { +// opacity: 1; +// outline: none; +// border-radius: 3px; +// } +// ` const CodeBlock = memo( ({ @@ -205,14 +205,13 @@ const CodeBlock = memo( initialWordWrap = true, initialWindowShade = true, collapsedHeight, - onLanguageChange, }: CodeBlockProps) => { - const [wordWrap, setWordWrap] = useState(initialWordWrap) - const [windowShade, setWindowShade] = useState(initialWindowShade) + const [wordWrap, _setWordWrap] = useState(initialWordWrap) + const [windowShade, _setWindowShade] = useState(initialWindowShade) const [currentLanguage, setCurrentLanguage] = useState(() => normalizeLanguage(language)) const userChangedLanguageRef = useRef(false) const [highlightedCode, setHighlightedCode] = useState(null) - const [showCollapseButton, setShowCollapseButton] = useState(true) + const [_showCollapseButton, setShowCollapseButton] = useState(true) const [isHovered, setIsHovered] = useState(false) const codeBlockRef = useRef(null) const preRef = useRef(null) @@ -616,7 +615,7 @@ const CodeBlock = memo( gap: 0, zIndex: 10, }}> - setWordWrap(!wordWrap)}> {wordWrap ? : } - + */} {showCopyFeedback ? : } diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 3c2ff9cc28..1cdaa2f4fe 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -984,12 +984,14 @@ const SettingsView = forwardRef(({ onDone, t - onConfirmDialogResult(false)}> - {t("settings:unsavedChangesDialog.cancelButton")} - - onConfirmDialogResult(true)}> - {t("settings:unsavedChangesDialog.discardButton")} - +
+ onConfirmDialogResult(false)}> + {t("settings:unsavedChangesDialog.cancelButton")} + + onConfirmDialogResult(true)}> + {t("settings:unsavedChangesDialog.discardButton")} + +
diff --git a/webview-ui/src/components/ui/alert-dialog.tsx b/webview-ui/src/components/ui/alert-dialog.tsx index b117868321..de2b04ac5b 100644 --- a/webview-ui/src/components/ui/alert-dialog.tsx +++ b/webview-ui/src/components/ui/alert-dialog.tsx @@ -36,7 +36,7 @@ function AlertDialogContent({ className, ...props }: React.ComponentProps + created: number + owned_by: string + pricing: { + prompt?: string + completion?: string + image?: string + request?: string + input_cache_reads?: string + input_cache_writes?: string + } +} + +const KILO_CODE_MODELS: Record = { + "axon-code": { + id: "axon-code", + name: "Axon Code", + description: "Axon Code is super intelligent LLM model for coding tasks", + input_modalities: ["text"], + context_length: 256000, + max_output_length: 32768, + output_modalities: ["text"], + supported_sampling_parameters: [ + "temperature", + "top_p", + "top_k", + "repetition_penalty", + "frequency_penalty", + "presence_penalty", + "seed", + "stop", + ], + supported_features: ["tools", "structured_outputs", "web_search"], + openrouter: { + slug: "matterai/axon", + }, + datacenters: [{ country_code: "US" }], + created: 1750426201, + owned_by: "matterai", + pricing: { + prompt: "0.000001", + completion: "0.000004", + image: "0", + request: "0", + input_cache_reads: "0", + input_cache_writes: "0", + }, + }, + "axon-code-pro": { + id: "axon-code-pro", + name: "Axon Code Pro", + description: "Axon Code Pro is a heavy, powerfull and super intelligent LLM model for coding tasks", + input_modalities: ["text"], + context_length: 192000, + max_output_length: 192000, + output_modalities: ["text"], + supported_sampling_parameters: [ + "temperature", + "top_p", + "top_k", + "repetition_penalty", + "frequency_penalty", + "presence_penalty", + "seed", + "stop", + ], + supported_features: ["tools", "structured_outputs", "web_search"], + openrouter: { + slug: "matterai/axon-code-pro", + }, + datacenters: [{ country_code: "US" }], + created: 1762009846, + owned_by: "matterai", + pricing: { + prompt: "0.000002", + completion: "0.000008", + image: "0", + request: "0", + input_cache_reads: "0", + input_cache_writes: "0", + }, + }, + axon: { + id: "axon", + name: "Axon", + description: "Axon is an general purpose super intelligent LLM model for high-effort day-to-day tasks", + input_modalities: ["text"], + context_length: 256000, + max_output_length: 32768, + output_modalities: ["text"], + supported_sampling_parameters: [ + "temperature", + "top_p", + "top_k", + "repetition_penalty", + "frequency_penalty", + "presence_penalty", + "seed", + "stop", + ], + supported_features: ["tools", "structured_outputs", "web_search"], + openrouter: { + slug: "matterai/axon", + }, + datacenters: [{ country_code: "US" }], + created: 1750426201, + owned_by: "matterai", + pricing: { + prompt: "0.000001", + completion: "0.000004", + image: "0", + request: "0", + input_cache_reads: "0", + input_cache_writes: "0", + }, + }, + "axon-mini": { + id: "axon-mini", + name: "Axon Mini", + description: + "Axon Mini is an general purpose super intelligent LLM coding model for low-effort day-to-day tasks", + input_modalities: ["text"], + context_length: 256000, + max_output_length: 16384, + output_modalities: ["text"], + supported_sampling_parameters: [ + "temperature", + "top_p", + "top_k", + "repetition_penalty", + "frequency_penalty", + "presence_penalty", + "seed", + "stop", + ], + supported_features: ["tools", "structured_outputs", "web_search"], + openrouter: { + slug: "matterai/axon", + }, + datacenters: [{ country_code: "US" }], + created: 1750426201, + owned_by: "matterai", + pricing: { + prompt: "2.5e-7", + completion: "0.000001", + image: "0", + request: "0", + input_cache_reads: "0", + input_cache_writes: "0", + }, + }, +} + +const parsePrice = (value?: string): number | undefined => { + if (typeof value === "undefined") { + return undefined + } + + const trimmed = value.trim() + if (trimmed.length === 0) { + return undefined + } + + const parsed = Number(trimmed) + return Number.isFinite(parsed) ? parsed : undefined +} + +const getSupportedParameters = (parameters: string[]): ModelParameter[] | undefined => { + const supported = parameters.filter((parameter): parameter is ModelParameter => isModelParameter(parameter)) + + return supported.length > 0 ? supported : undefined +} + +const toOpenRouterModelProvider = (model: KiloCodeModel): OpenRouterModelProvider => { + const cacheReadsPrice = parsePrice(model.pricing.input_cache_reads) + const cacheWritesPrice = parsePrice(model.pricing.input_cache_writes) + const supportedParameters = getSupportedParameters(model.supported_sampling_parameters) + const supportsTemperature = supportedParameters?.includes("temperature") + const datacenterLabel = model.datacenters + .map(({ country_code }) => country_code) + .filter(Boolean) + .join(", ") + + return { + maxTokens: model.max_output_length, + contextWindow: model.context_length, + maxThinkingTokens: undefined, + supportsImages: model.output_modalities.includes("image"), + supportsPromptCache: typeof cacheReadsPrice !== "undefined", + supportsVerbosity: undefined, + supportsReasoningBudget: undefined, + requiredReasoningBudget: undefined, + supportsReasoningEffort: undefined, + supportedParameters, + supportsTemperature, + inputPrice: parsePrice(model.pricing.prompt), + outputPrice: parsePrice(model.pricing.completion), + cacheWritesPrice, + cacheReadsPrice, + description: model.description, + reasoningEffort: undefined, + minTokensPerCachePoint: undefined, + maxCachePoints: undefined, + cachableFields: undefined, + displayName: model.name, + preferredIndex: undefined, + deprecated: undefined, + label: datacenterLabel ? `KiloCode (${datacenterLabel})` : "KiloCode", + } +} + // kilocode_change: baseUrl, apiKey -async function getOpenRouterProvidersForModel(modelId: string, baseUrl?: string, apiKey?: string) { +async function getOpenRouterProvidersForModel(modelId: string, _baseUrl?: string, _apiKey?: string) { const models: Record = {} - try { - // kilocode_change start: baseUrl, apiKey - const response = await axios.get( - `${baseUrl?.trim() || "https://api.matterai.so/v1/web"}/models/${modelId}`, - { headers: { Authorization: `Bearer ${apiKey}` }, timeout: 60000 }, // 60 seconds timeout - ) - - // console.log("response", response) - // // kilocode_change end - // const result = openRouterModelSchema.safeParse(response.data) - - // console.log("result", result) - - // if (!result.success) { - // console.error("OpenRouter API response validation failed:", result.error) - // return models - // } - - // const { description, architecture, context_length, max_completion_tokens, pricing, provider_name, tag } = result.data.data - - // // Skip image generation models (models that output images) - // if (architecture?.output_modalities?.includes("image")) { - // return models - // } - - // const providerName = tag ?? provider_name // kilocode_change - // const inputPrice = parseApiPrice(pricing?.prompt) - // const outputPrice = parseApiPrice(pricing?.completion) - // const cacheReadsPrice = parseApiPrice(pricing?.input_cache_read) - // const cacheWritesPrice = parseApiPrice(pricing?.input_cache_write) - - // const modelInfo: OpenRouterModelProvider = { - // maxTokens: max_completion_tokens || context_length, - // contextWindow: context_length, - // supportsImages: architecture?.input_modalities?.includes("image") ?? false, - // supportsPromptCache: typeof cacheReadsPrice !== "undefined", - // cacheReadsPrice, - // cacheWritesPrice, - // inputPrice, - // outputPrice, - // description, - // label: providerName, - // } - - // // TODO: This is wrong. We need to fetch the model info from - // // OpenRouter instead of hardcoding it here. The endpoints payload - // // doesn't include this unfortunately, so we need to get it from the - // // main models endpoint. - // // Removed switch statement as we only have 1 provider - - models["KiloCode"] = response.data - } catch (error) { - if (error instanceof z.ZodError) { - console.error(`OpenRouter API response validation failed:`, error.errors) - } else { - console.error(`Error fetching OpenRouter providers:`, error) - } + const model = KILO_CODE_MODELS[modelId] + if (!model) { + return models } + models["KiloCode"] = toOpenRouterModelProvider(model) + return models } diff --git a/webview-ui/src/index.css b/webview-ui/src/index.css index 38566ec194..5788d6fd12 100644 --- a/webview-ui/src/index.css +++ b/webview-ui/src/index.css @@ -207,7 +207,9 @@ /* Code Block Styles */ pre, code { - background-color: var(--vscode-textCodeBlock-background); + background-color: var(--vscode-input-background) !important; + padding-left: 0.1rem; + padding-right: 0.1rem; } /* Search result highlighting */