mirror of
https://github.com/cline/cline.git
synced 2026-09-07 12:58:33 +08:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1ec4a65bf | ||
|
|
8b4822c0dc |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
More user-friendly MCP Services editing
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "claude-dev",
|
||||
"version": "3.8.2",
|
||||
"version": "3.8.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "claude-dev",
|
||||
"version": "3.8.2",
|
||||
"version": "3.8.4",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/bedrock-sdk": "^0.12.4",
|
||||
|
||||
@@ -25,7 +25,7 @@ import { ChatContent } from "../../shared/ChatContent"
|
||||
import { ChatSettings, DEFAULT_CHAT_SETTINGS } from "../../shared/ChatSettings"
|
||||
import { ExtensionMessage, ExtensionState, Invoke, Platform } from "../../shared/ExtensionMessage"
|
||||
import { HistoryItem } from "../../shared/HistoryItem"
|
||||
import { McpDownloadResponse, McpMarketplaceCatalog, McpServer } from "../../shared/mcp"
|
||||
import { McpConfig, McpDownloadResponse, McpMarketplaceCatalog, McpServer, UpdateMcpConfig } from "../../shared/mcp"
|
||||
import { ClineCheckpointRestore, WebviewMessage } from "../../shared/WebviewMessage"
|
||||
import { fileExistsAtPath } from "../../utils/fs"
|
||||
import { searchCommits } from "../../utils/git"
|
||||
@@ -746,6 +746,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
}
|
||||
break
|
||||
}
|
||||
case "updateMcpServices": {
|
||||
await this.updateMcpServices(message.newMcpConfig)
|
||||
break
|
||||
}
|
||||
case "fetchMcpMarketplace": {
|
||||
await this.fetchMcpMarketplace(message.bool)
|
||||
break
|
||||
@@ -1451,7 +1455,46 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
console.error("Failed to silently refresh MCP marketplace:", error)
|
||||
}
|
||||
}
|
||||
|
||||
private async updateMcpServices(newMcpConfig?: UpdateMcpConfig) {
|
||||
if (!newMcpConfig) {
|
||||
return
|
||||
}
|
||||
if (this.mcpHub) {
|
||||
const { name, mcpConfig } = newMcpConfig
|
||||
const mcpPath = await this.mcpHub?.getMcpSettingsFilePath()
|
||||
try {
|
||||
const exists = await fileExistsAtPath(mcpPath)
|
||||
let mcpServers = {
|
||||
[name]: mcpConfig,
|
||||
}
|
||||
if (exists) {
|
||||
mcpServers = JSON.parse(await fs.readFile(mcpPath, "utf8")).mcpServers || {}
|
||||
if (mcpServers[name]) {
|
||||
if (mcpConfig.command) {
|
||||
delete mcpServers[name].url
|
||||
delete mcpServers[name].headers
|
||||
} else {
|
||||
delete mcpServers[name].command
|
||||
delete mcpServers[name].args
|
||||
delete mcpServers[name].env
|
||||
}
|
||||
mcpServers[name] = {
|
||||
...mcpServers[name],
|
||||
...mcpConfig,
|
||||
}
|
||||
} else {
|
||||
mcpServers[name] = mcpConfig
|
||||
}
|
||||
}
|
||||
await fs.writeFile(mcpPath, JSON.stringify({ mcpServers }, null, 2))
|
||||
if (this.mcpHub) {
|
||||
this.mcpHub?.onMcpSettingsFileChange()
|
||||
}
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(`Unable to create or open: ${JSON.stringify(error)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
private async fetchMcpMarketplace(forceRefresh: boolean = false) {
|
||||
try {
|
||||
// Check if we have cached data
|
||||
|
||||
+14
-12
@@ -154,22 +154,24 @@ export class McpHub {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
public async onMcpSettingsFileChange(): Promise<void> {
|
||||
const settings = await this.readAndValidateMcpSettingsFile()
|
||||
if (settings) {
|
||||
try {
|
||||
vscode.window.showInformationMessage("Updating MCP servers...")
|
||||
await this.updateServerConnections(settings.mcpServers)
|
||||
vscode.window.showInformationMessage("MCP servers updated")
|
||||
} catch (error) {
|
||||
console.error("Failed to process MCP settings change:", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
private async watchMcpSettingsFile(): Promise<void> {
|
||||
const settingsPath = await this.getMcpSettingsFilePath()
|
||||
this.disposables.push(
|
||||
vscode.workspace.onDidSaveTextDocument(async (document) => {
|
||||
if (arePathsEqual(document.uri.fsPath, settingsPath)) {
|
||||
const settings = await this.readAndValidateMcpSettingsFile()
|
||||
if (settings) {
|
||||
try {
|
||||
vscode.window.showInformationMessage("Updating MCP servers...")
|
||||
await this.updateServerConnections(settings.mcpServers)
|
||||
vscode.window.showInformationMessage("MCP servers updated")
|
||||
} catch (error) {
|
||||
console.error("Failed to process MCP settings change:", error)
|
||||
}
|
||||
}
|
||||
await this.onMcpSettingsFileChange()
|
||||
}
|
||||
}),
|
||||
)
|
||||
@@ -274,7 +276,7 @@ export class McpHub {
|
||||
}
|
||||
|
||||
// Connect
|
||||
await client.connect(transport)
|
||||
client.connect(transport)
|
||||
connection.server.status = "connected"
|
||||
connection.server.error = ""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ChatSettings } from "./ChatSettings"
|
||||
import { UserInfo } from "./UserInfo"
|
||||
import { ChatContent } from "./ChatContent"
|
||||
import { TelemetrySetting } from "./TelemetrySetting"
|
||||
import { McpConfig, UpdateMcpConfig } from "./mcp"
|
||||
|
||||
export interface WebviewMessage {
|
||||
type:
|
||||
@@ -12,6 +13,7 @@ export interface WebviewMessage {
|
||||
| "webviewDidLaunch"
|
||||
| "newTask"
|
||||
| "askResponse"
|
||||
| "updateMcpServices"
|
||||
| "clearTask"
|
||||
| "didShowAnnouncement"
|
||||
| "selectImages"
|
||||
@@ -67,6 +69,7 @@ export interface WebviewMessage {
|
||||
| "requestTotalTasksSize"
|
||||
// | "relaunchChromeDebugMode"
|
||||
text?: string
|
||||
newMcpConfig?: UpdateMcpConfig
|
||||
disabled?: boolean
|
||||
askResponse?: ClineAskResponse
|
||||
apiConfiguration?: ApiConfiguration
|
||||
|
||||
@@ -1,7 +1,25 @@
|
||||
export const DEFAULT_MCP_TIMEOUT_SECONDS = 60 // matches Anthropic's default timeout in their MCP SDK
|
||||
export const MIN_MCP_TIMEOUT_SECONDS = 1
|
||||
export type McpMode = "full" | "server-use-only" | "off"
|
||||
export type McpConfig = {
|
||||
url?: string
|
||||
headers?: {
|
||||
[k: string]: string
|
||||
}
|
||||
command?: string
|
||||
args?: string[]
|
||||
env?: {
|
||||
[k: string]: string
|
||||
}
|
||||
disabled?: boolean
|
||||
timeout?: number
|
||||
alwaysAllow?: string[]
|
||||
}
|
||||
|
||||
export type UpdateMcpConfig = {
|
||||
name: string
|
||||
mcpConfig: McpConfig
|
||||
}
|
||||
export type McpServer = {
|
||||
name: string
|
||||
config: string
|
||||
|
||||
Generated
+494
-4
@@ -9,14 +9,18 @@
|
||||
"version": "0.3.0",
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.27.4",
|
||||
"@radix-ui/react-dialog": "^1.1.6",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@vscode/webview-ui-toolkit": "^1.4.0",
|
||||
"classnames": "^2.5.1",
|
||||
"clsx": "^2.1.1",
|
||||
"debounce": "^2.1.1",
|
||||
"dompurify": "^3.2.4",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"firebase": "^11.3.0",
|
||||
"fuse.js": "^7.0.0",
|
||||
"fzf": "^0.5.2",
|
||||
"lucide-react": "^0.484.0",
|
||||
"mermaid": "^11.4.1",
|
||||
"posthog-js": "^1.224.0",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
@@ -28,7 +32,8 @@
|
||||
"react-use": "^17.6.0",
|
||||
"react-virtuoso": "^4.12.3",
|
||||
"rehype-highlight": "^7.0.1",
|
||||
"styled-components": "^6.1.15"
|
||||
"styled-components": "^6.1.15",
|
||||
"tailwind-merge": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.17.0",
|
||||
@@ -2113,6 +2118,318 @@
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@radix-ui/primitive": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
||||
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
||||
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
|
||||
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dialog": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz",
|
||||
"integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.1",
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-dismissable-layer": "1.1.5",
|
||||
"@radix-ui/react-focus-guards": "1.1.1",
|
||||
"@radix-ui/react-focus-scope": "1.1.2",
|
||||
"@radix-ui/react-id": "1.1.0",
|
||||
"@radix-ui/react-portal": "1.1.4",
|
||||
"@radix-ui/react-presence": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.0.2",
|
||||
"@radix-ui/react-slot": "1.1.2",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||
"aria-hidden": "^1.2.4",
|
||||
"react-remove-scroll": "^2.6.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dismissable-layer": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz",
|
||||
"integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.1",
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-primitive": "2.0.2",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||
"@radix-ui/react-use-escape-keydown": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-focus-guards": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz",
|
||||
"integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-focus-scope": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz",
|
||||
"integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-primitive": "2.0.2",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-id": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz",
|
||||
"integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-portal": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz",
|
||||
"integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.0.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-presence": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
|
||||
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||
"integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz",
|
||||
"integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-escape-keydown": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
|
||||
"integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz",
|
||||
"integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.34.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.2.tgz",
|
||||
@@ -3488,14 +3805,14 @@
|
||||
"version": "15.7.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
|
||||
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
|
||||
"integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
@@ -3506,7 +3823,7 @@
|
||||
"version": "18.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz",
|
||||
"integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^18.0.0"
|
||||
@@ -4031,6 +4348,18 @@
|
||||
"dev": true,
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/aria-hidden": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz",
|
||||
"integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/aria-query": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
|
||||
@@ -4251,6 +4580,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/classnames": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
||||
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
@@ -4265,6 +4600,15 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -5019,6 +5363,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-node-es": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/devlop": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
|
||||
@@ -5673,6 +6023,15 @@
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-nonce": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
|
||||
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||
@@ -6695,6 +7054,15 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "0.484.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.484.0.tgz",
|
||||
"integrity": "sha512-oZy8coK9kZzvqhSgfbGkPtTgyjpBvs3ukLgDPv14dSOZtBtboryWF5o8i3qen7QbGg7JhiJBz5mK1p8YoMZTLQ==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lz-string": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
|
||||
@@ -7571,6 +7939,75 @@
|
||||
"react": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-remove-scroll": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz",
|
||||
"integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-remove-scroll-bar": "^2.3.7",
|
||||
"react-style-singleton": "^2.2.3",
|
||||
"tslib": "^2.1.0",
|
||||
"use-callback-ref": "^1.3.3",
|
||||
"use-sidecar": "^1.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-remove-scroll-bar": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
|
||||
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-style-singleton": "^2.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-style-singleton": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
|
||||
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"get-nonce": "^1.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-textarea-autosize": {
|
||||
"version": "8.5.7",
|
||||
"resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.7.tgz",
|
||||
@@ -8275,6 +8712,16 @@
|
||||
"integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tailwind-merge": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz",
|
||||
"integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/dcastil"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.0.12",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.12.tgz",
|
||||
@@ -8789,6 +9236,27 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-callback-ref": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
|
||||
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-composed-ref": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz",
|
||||
@@ -8834,6 +9302,28 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sidecar": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
||||
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-node-es": "^1.1.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vfile": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
||||
|
||||
@@ -16,12 +16,17 @@
|
||||
"@floating-ui/react": "^0.27.4",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@vscode/webview-ui-toolkit": "^1.4.0",
|
||||
"@radix-ui/react-dialog": "^1.1.6",
|
||||
"lucide-react": "^0.484.0",
|
||||
"classnames": "^2.5.1",
|
||||
"debounce": "^2.1.1",
|
||||
"dompurify": "^3.2.4",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"firebase": "^11.3.0",
|
||||
"fuse.js": "^7.0.0",
|
||||
"fzf": "^0.5.2",
|
||||
"clsx": "^2.1.1",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"mermaid": "^11.4.1",
|
||||
"posthog-js": "^1.224.0",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "../ui/dialog"
|
||||
|
||||
import styled from "styled-components"
|
||||
import { McpConfig } from "../../../../src/shared/mcp"
|
||||
|
||||
import { vscode } from "../../utils/vscode"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
interface McpCreateDialogProps {
|
||||
open: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
mcpConfig?: McpConfig
|
||||
mcpName?: string
|
||||
}
|
||||
|
||||
const McpCreateDialog = (props: McpCreateDialogProps) => {
|
||||
const { open, onOpenChange, mcpConfig, mcpName } = props
|
||||
const [name, setName] = useState<string>()
|
||||
const [command, setCommand] = useState<string>("npx")
|
||||
const [args, setArgs] = useState<string[]>([])
|
||||
const [argsKey, setArgsKey] = useState<string>("")
|
||||
const [env, setEnv] = useState<Record<string, string>>({})
|
||||
const [envKey, setEnvKey] = useState<string>("")
|
||||
const [envValue, setEnvValue] = useState<string>("")
|
||||
|
||||
useEffect(() => {
|
||||
if (open && mcpConfig) {
|
||||
mcpConfig.args && setArgs(mcpConfig.args)
|
||||
mcpConfig.command && setCommand(mcpConfig.command)
|
||||
mcpConfig.env && setEnv(mcpConfig.env)
|
||||
}
|
||||
if (open && mcpName) {
|
||||
setName(mcpName)
|
||||
}
|
||||
if (!open) {
|
||||
setName("")
|
||||
setCommand("npx")
|
||||
setArgs([])
|
||||
setArgsKey("")
|
||||
setEnv({})
|
||||
setEnvKey("")
|
||||
setEnvValue("")
|
||||
}
|
||||
}, [open, mcpConfig, mcpName])
|
||||
const handleAddArgs = () => {
|
||||
if (argsKey && !args.includes(argsKey)) {
|
||||
const newArgs = [...args, argsKey]
|
||||
setArgs(newArgs)
|
||||
setArgsKey("")
|
||||
}
|
||||
}
|
||||
const handleAddEnv = () => {
|
||||
if (envKey && envValue) {
|
||||
const newEnv = { ...env, [envKey]: envValue }
|
||||
setEnv(newEnv)
|
||||
setEnvKey("")
|
||||
setEnvValue("")
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{mcpConfig ? "Edit" : "Add"} MCP Configuration</DialogTitle>
|
||||
<DialogDescription>
|
||||
<div style={{ width: "100%" }}>
|
||||
<VSCodeTextField
|
||||
value={name || ""}
|
||||
onInput={(e: any) => {
|
||||
setName(e.target.value)
|
||||
}}
|
||||
placeholder={"Enter MCP name"}
|
||||
disabled={!!mcpName}
|
||||
className="w-full">
|
||||
<div className="flex justify-between items-center mb-1">
|
||||
<label className="block font-medium">MCP Name</label>
|
||||
</div>
|
||||
</VSCodeTextField>
|
||||
<VSCodeTextField
|
||||
value={command || ""}
|
||||
onInput={(e: any) => {
|
||||
setCommand(e.target.value)
|
||||
}}
|
||||
placeholder={"Enter command"}
|
||||
className="w-full">
|
||||
<div className="flex justify-between items-center mb-1">
|
||||
<label className="block font-medium">CLI command for running this MCP service</label>
|
||||
</div>
|
||||
</VSCodeTextField>
|
||||
<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
|
||||
<div>
|
||||
<label className="block font-medium mb-1" data-testid="allowed-commands-heading">
|
||||
Command Arguments
|
||||
</label>
|
||||
<div className="text-vscode-descriptionForeground text-sm mt-1">
|
||||
Example: When command arguments are set to ./mcp.js and -p, and the command is set to
|
||||
node, the local MCP will start as: node ./mcp.js -p
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<VSCodeTextField
|
||||
value={argsKey}
|
||||
onInput={(e: any) => setArgsKey(e.target.value)}
|
||||
placeholder="Enter command argument"
|
||||
className="grow"
|
||||
data-testid="command-input"
|
||||
/>
|
||||
<VSCodeButton
|
||||
onClick={handleAddArgs}
|
||||
style={{ minWidth: 60 }}
|
||||
data-testid="add-command-button">
|
||||
Add
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{(args ?? []).map((cmd, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border border-vscode-input-border bg-primary text-primary-foreground flex items-center gap-1 rounded-xs px-1.5 p-0.5">
|
||||
<span>{cmd}</span>
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
className="text-primary-foreground"
|
||||
data-testid={`remove-command-${index}`}
|
||||
onClick={() => {
|
||||
const newArgs = (args ?? []).filter((_, i) => i !== index)
|
||||
setArgs(newArgs)
|
||||
}}>
|
||||
<span className="codicon codicon-close" />
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
|
||||
<div>
|
||||
<label className="block font-medium mb-1" data-testid="allowed-commands-heading">
|
||||
env
|
||||
</label>
|
||||
<div className="text-vscode-descriptionForeground text-sm mt-1">
|
||||
Example: When this local tool needs an API key, you can configure it as
|
||||
"OPENWEATHER_API_KEY": "your-api-key"
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<VSCodeTextField
|
||||
value={envKey}
|
||||
onInput={(e: any) => setEnvKey(e.target.value)}
|
||||
placeholder="Enter key"
|
||||
className="grow"
|
||||
data-testid="command-input"
|
||||
/>
|
||||
<VSCodeTextField
|
||||
value={envValue}
|
||||
onInput={(e: any) => setEnvValue(e.target.value)}
|
||||
placeholder="Enter value"
|
||||
className="grow"
|
||||
data-testid="command-input"
|
||||
/>
|
||||
<VSCodeButton
|
||||
onClick={handleAddEnv}
|
||||
style={{ minWidth: 60 }}
|
||||
data-testid="add-command-button">
|
||||
Add
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Object.entries(env || []).map(([key, value], index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border border-vscode-input-border bg-primary text-primary-foreground flex items-center gap-1 rounded-xs px-1.5 p-0.5">
|
||||
<span>
|
||||
"{key}": "{value}"
|
||||
</span>
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
className="text-primary-foreground"
|
||||
data-testid={`remove-command-${index}`}
|
||||
onClick={() => {
|
||||
delete env[key]
|
||||
setEnv({ ...env })
|
||||
}}>
|
||||
<span className="codicon codicon-close" />
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<VSCodeButton appearance="secondary" onClick={() => onOpenChange?.(false)}>
|
||||
Cancel
|
||||
</VSCodeButton>
|
||||
<VSCodeButton
|
||||
appearance="primary"
|
||||
disabled={!name || !command}
|
||||
onClick={() => {
|
||||
vscode.postMessage({
|
||||
type: "updateMcpServices",
|
||||
newMcpConfig: {
|
||||
name: name!,
|
||||
mcpConfig: {
|
||||
...mcpConfig,
|
||||
command,
|
||||
args,
|
||||
env,
|
||||
},
|
||||
},
|
||||
})
|
||||
onOpenChange?.(false)
|
||||
}}>
|
||||
Confirm
|
||||
</VSCodeButton>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default McpCreateDialog
|
||||
@@ -18,6 +18,7 @@ import McpMarketplaceView from "./marketplace/McpMarketplaceView"
|
||||
import McpResourceRow from "./McpResourceRow"
|
||||
import McpToolRow from "./McpToolRow"
|
||||
import DangerButton from "../common/DangerButton"
|
||||
import McpCreateDialog from "./McpCreateDialog"
|
||||
|
||||
type McpViewProps = {
|
||||
onDone: () => void
|
||||
@@ -26,7 +27,7 @@ type McpViewProps = {
|
||||
const McpView = ({ onDone }: McpViewProps) => {
|
||||
const { mcpServers: servers, mcpMarketplaceEnabled } = useExtensionState()
|
||||
const [activeTab, setActiveTab] = useState(mcpMarketplaceEnabled ? "marketplace" : "installed")
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
const handleTabChange = (tab: string) => {
|
||||
setActiveTab(tab)
|
||||
}
|
||||
@@ -112,6 +113,13 @@ const McpView = ({ onDone }: McpViewProps) => {
|
||||
<VSCodeLink href="https://x.com/sdrzn/status/1867271665086074969" style={{ display: "inline" }}>
|
||||
See a demo here.
|
||||
</VSCodeLink>
|
||||
<VSCodeLink
|
||||
onClick={() => {
|
||||
vscode.postMessage({ type: "openMcpSettings" })
|
||||
}}
|
||||
style={{ display: "inline" }}>
|
||||
Configure MCP Servers JSON here.
|
||||
</VSCodeLink>
|
||||
</div>
|
||||
|
||||
{servers.length > 0 ? (
|
||||
@@ -139,17 +147,17 @@ const McpView = ({ onDone }: McpViewProps) => {
|
||||
No MCP servers installed
|
||||
</div>
|
||||
)}
|
||||
|
||||
<McpCreateDialog open={open} onOpenChange={setOpen} />
|
||||
{/* Settings Section */}
|
||||
<div style={{ marginBottom: "20px", marginTop: 10 }}>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
style={{ width: "100%", marginBottom: "5px" }}
|
||||
onClick={() => {
|
||||
vscode.postMessage({ type: "openMcpSettings" })
|
||||
setOpen(true)
|
||||
}}>
|
||||
<span className="codicon codicon-server" style={{ marginRight: "6px" }}></span>
|
||||
Configure MCP Servers
|
||||
Add MCP Servers
|
||||
</VSCodeButton>
|
||||
|
||||
<div style={{ textAlign: "center" }}>
|
||||
@@ -209,7 +217,7 @@ const ServerRow = ({ server }: { server: McpServer }) => {
|
||||
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
const [isDeleting, setIsDeleting] = useState(false)
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
const getStatusColor = () => {
|
||||
switch (server.status) {
|
||||
case "connected":
|
||||
@@ -540,6 +548,7 @@ const ServerRow = ({ server }: { server: McpServer }) => {
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
<McpCreateDialog open={open} onOpenChange={setOpen} mcpConfig={JSON.parse(server.config)} mcpName={server.name} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import * as React from "react"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
import { XIcon } from "lucide-react"
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
function classnames(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
||||
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
||||
}
|
||||
|
||||
function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
||||
}
|
||||
|
||||
function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
||||
}
|
||||
|
||||
function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
||||
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
||||
}
|
||||
|
||||
function DialogOverlay({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<DialogPrimitive.Overlay
|
||||
data-slot="dialog-overlay"
|
||||
className={classnames(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogContent({ className, children, ...props }: React.ComponentProps<typeof DialogPrimitive.Content>) {
|
||||
return (
|
||||
<DialogPortal data-slot="dialog-portal">
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
data-slot="dialog-content"
|
||||
className={classnames(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className,
|
||||
)}
|
||||
{...props}>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
|
||||
<XIcon />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-header"
|
||||
className={classnames("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-footer"
|
||||
className={classnames("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
||||
return (
|
||||
<DialogPrimitive.Title
|
||||
data-slot="dialog-title"
|
||||
className={classnames("text-lg leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogDescription({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||
return (
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={classnames("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
}
|
||||
@@ -2,8 +2,156 @@
|
||||
@import "tailwindcss/theme.css" layer(theme);
|
||||
/* Disable Tailwind's CSS reset to preserve existing styles */
|
||||
/* @import "tailwindcss/preflight.css" layer(base); */
|
||||
@import "./preflight.css" layer(base);
|
||||
|
||||
@import "tailwindcss/utilities.css" layer(utilities);
|
||||
|
||||
@theme {
|
||||
--font-display: var(--vscode-font-family);
|
||||
|
||||
--text-xs: calc(var(--vscode-font-size) * 0.85);
|
||||
--text-sm: calc(var(--vscode-font-size) * 0.9);
|
||||
--text-base: var(--vscode-font-size);
|
||||
--text-lg: calc(var(--vscode-font-size) * 1.1);
|
||||
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
|
||||
/**
|
||||
* Allow VSCode colors to be used with Tailwind.
|
||||
*/
|
||||
|
||||
--color-vscode-foreground: var(--vscode-foreground);
|
||||
|
||||
--color-vscode-editor-foreground: var(--vscode-editor-foreground);
|
||||
--color-vscode-editor-background: var(--vscode-editor-background);
|
||||
|
||||
--color-vscode-editorGroup-border: var(--vscode-editorGroup-border);
|
||||
|
||||
--color-vscode-button-foreground: var(--vscode-button-foreground);
|
||||
--color-vscode-button-background: var(--vscode-button-background);
|
||||
--color-vscode-button-secondaryForeground: var(--vscode-button-secondaryForeground);
|
||||
--color-vscode-button-secondaryBackground: var(--vscode-button-secondaryBackground);
|
||||
|
||||
--color-vscode-dropdown-foreground: var(--vscode-dropdown-foreground);
|
||||
--color-vscode-dropdown-background: var(--vscode-dropdown-background);
|
||||
--color-vscode-dropdown-border: var(--vscode-dropdown-border);
|
||||
|
||||
--color-vscode-input-foreground: var(--vscode-input-foreground);
|
||||
--color-vscode-input-background: var(--vscode-input-background);
|
||||
--color-vscode-input-border: var(--vscode-input-border);
|
||||
|
||||
--color-vscode-focusBorder: var(--vscode-focusBorder);
|
||||
|
||||
--color-vscode-badge-foreground: var(--vscode-badge-foreground);
|
||||
--color-vscode-badge-background: var(--vscode-badge-background);
|
||||
|
||||
--color-vscode-notifications-foreground: var(--vscode-notifications-foreground);
|
||||
--color-vscode-notifications-background: var(--vscode-notifications-background);
|
||||
--color-vscode-notifications-border: var(--vscode-notifications-border);
|
||||
|
||||
--color-vscode-descriptionForeground: var(--vscode-descriptionForeground);
|
||||
--color-vscode-errorForeground: var(--vscode-errorForeground);
|
||||
|
||||
--color-vscode-list-hoverForeground: var(--vscode-list-hoverForeground);
|
||||
--color-vscode-list-hoverBackground: var(--vscode-list-hoverBackground);
|
||||
--color-vscode-list-focusBackground: var(--vscode-list-focusBackground);
|
||||
--color-vscode-list-activeSelectionBackground: var(--vscode-list-activeSelectionBackground);
|
||||
--color-vscode-list-activeSelectionForeground: var(--vscode-list-activeSelectionForeground);
|
||||
|
||||
--color-vscode-toolbar-hoverBackground: var(--vscode-toolbar-hoverBackground);
|
||||
|
||||
--color-vscode-panel-border: var(--vscode-panel-border);
|
||||
|
||||
--color-vscode-sideBar-foreground: var(--vscode-sideBar-foreground);
|
||||
--color-vscode-sideBar-background: var(--vscode-sideBar-background);
|
||||
--color-vscode-sideBar-border: var(--vscode-sideBar-border);
|
||||
|
||||
--color-vscode-sideBarSectionHeader-foreground: var(--vscode-sideBarSectionHeader-foreground);
|
||||
--color-vscode-sideBarSectionHeader-background: var(--vscode-sideBarSectionHeader-background);
|
||||
--color-vscode-sideBarSectionHeader-border: var(--vscode-sideBarSectionHeader-border);
|
||||
|
||||
--color-vscode-charts-green: var(--vscode-charts-green);
|
||||
--color-vscode-charts-yellow: var(--vscode-charts-yellow);
|
||||
|
||||
--color-vscode-inputValidation-infoForeground: var(--vscode-inputValidation-infoForeground);
|
||||
--color-vscode-inputValidation-infoBackground: var(--vscode-inputValidation-infoBackground);
|
||||
--color-vscode-inputValidation-infoBorder: var(--vscode-inputValidation-infoBorder);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: var(--vscode-editor-background);
|
||||
--foreground: var(--vscode-editor-foreground);
|
||||
--card: var(--vscode-editor-background);
|
||||
--card-foreground: var(--vscode-editor-foreground);
|
||||
--popover: var(--vscode-menu-background, var(--vscode-editor-background));
|
||||
--popover-foreground: var(--vscode-menu-foreground, var(--vscode-editor-foreground));
|
||||
--primary: var(--vscode-button-background);
|
||||
--primary-foreground: var(--vscode-button-foreground);
|
||||
--secondary: var(--vscode-button-secondaryBackground);
|
||||
--secondary-foreground: var(--vscode-button-secondaryForeground);
|
||||
--muted: var(--vscode-disabledForeground);
|
||||
--muted-foreground: var(--vscode-descriptionForeground);
|
||||
--accent: var(--vscode-list-hoverBackground);
|
||||
--accent-foreground: var(--vscode-list-hoverForeground);
|
||||
--destructive: var(--vscode-errorForeground);
|
||||
--destructive-foreground: var(--vscode-button-foreground);
|
||||
--border: var(--vscode-input-border);
|
||||
--input: var(--vscode-input-background);
|
||||
--ring: var(--vscode-input-border);
|
||||
--chart-1: var(--vscode-charts-red);
|
||||
--chart-2: var(--vscode-charts-blue);
|
||||
--chart-3: var(--vscode-charts-yellow);
|
||||
--chart-4: var(--vscode-charts-orange);
|
||||
--chart-5: var(--vscode-charts-green);
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* Border Styles */
|
||||
.border,
|
||||
.border-r,
|
||||
.border-l,
|
||||
.border-t,
|
||||
.border-b,
|
||||
.border-x,
|
||||
.border-y {
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
/* Code Block Styles */
|
||||
pre,
|
||||
code {
|
||||
background-color: var(--vscode-textCodeBlock-background);
|
||||
}
|
||||
}
|
||||
textarea:focus {
|
||||
outline: 1.5px solid var(--vscode-focusBorder, #007fd4);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
|
||||
2. Remove default margins and padding
|
||||
3. Reset all borders.
|
||||
*/
|
||||
|
||||
*,
|
||||
::after,
|
||||
::before,
|
||||
::backdrop,
|
||||
::file-selector-button {
|
||||
box-sizing: border-box; /* 1 */
|
||||
/* margin: 0; */ /* 2 */
|
||||
padding: 0; /* 2 */
|
||||
border: 0 solid; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use a consistent sensible line-height in all browsers.
|
||||
2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
3. Use a more readable tab size.
|
||||
4. Use the user's configured `sans` font-family by default.
|
||||
5. Use the user's configured `sans` font-feature-settings by default.
|
||||
6. Use the user's configured `sans` font-variation-settings by default.
|
||||
7. Disable tap highlights on iOS.
|
||||
*/
|
||||
|
||||
html,
|
||||
:host {
|
||||
line-height: 1.5; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
tab-size: 4; /* 3 */
|
||||
font-family: var(
|
||||
--default-font-family,
|
||||
ui-sans-serif,
|
||||
system-ui,
|
||||
sans-serif,
|
||||
"Apple Color Emoji",
|
||||
"Segoe UI Emoji",
|
||||
"Segoe UI Symbol",
|
||||
"Noto Color Emoji"
|
||||
); /* 4 */
|
||||
font-feature-settings: var(--default-font-feature-settings, normal); /* 5 */
|
||||
font-variation-settings: var(--default-font-variation-settings, normal); /* 6 */
|
||||
-webkit-tap-highlight-color: transparent; /* 7 */
|
||||
}
|
||||
|
||||
/*
|
||||
Inherit line-height from `html` so users can set them as a class directly on the `html` element.
|
||||
*/
|
||||
|
||||
body {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Add the correct height in Firefox.
|
||||
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
|
||||
3. Reset the default border style to a 1px solid border.
|
||||
*/
|
||||
|
||||
hr {
|
||||
height: 0; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
border-top-width: 1px; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct text decoration in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
abbr:where([title]) {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the default font size and weight for headings.
|
||||
*/
|
||||
|
||||
/* h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
} */
|
||||
|
||||
/*
|
||||
Reset links to optimize for opt-in styling instead of opt-out.
|
||||
*/
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
-webkit-text-decoration: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font weight in Edge and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use the user's configured `mono` font-family by default.
|
||||
2. Use the user's configured `mono` font-feature-settings by default.
|
||||
3. Use the user's configured `mono` font-variation-settings by default.
|
||||
4. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp,
|
||||
pre {
|
||||
font-family: var(
|
||||
--default-mono-font-family,
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
Menlo,
|
||||
Monaco,
|
||||
Consolas,
|
||||
"Liberation Mono",
|
||||
"Courier New",
|
||||
monospace
|
||||
); /* 4 */
|
||||
font-feature-settings: var(--default-mono-font-feature-settings, normal); /* 5 */
|
||||
font-variation-settings: var(--default-mono-font-variation-settings, normal); /* 6 */
|
||||
font-size: 1em; /* 4 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
|
||||
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
|
||||
3. Remove gaps between table borders by default.
|
||||
*/
|
||||
|
||||
table {
|
||||
text-indent: 0; /* 1 */
|
||||
border-color: inherit; /* 2 */
|
||||
border-collapse: collapse; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Use the modern Firefox focus style for all focusable elements.
|
||||
*/
|
||||
|
||||
:-moz-focusring {
|
||||
outline: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct vertical alignment in Chrome and Firefox.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct display in Chrome and Safari.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/*
|
||||
Make lists unstyled by default.
|
||||
*/
|
||||
|
||||
ol,
|
||||
ul,
|
||||
menu {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
|
||||
This can trigger a poorly considered lint error in some tools but is included by design.
|
||||
*/
|
||||
|
||||
img,
|
||||
svg,
|
||||
video,
|
||||
canvas,
|
||||
audio,
|
||||
iframe,
|
||||
embed,
|
||||
object {
|
||||
display: block; /* 1 */
|
||||
vertical-align: middle; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
*/
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Inherit font styles in all browsers.
|
||||
2. Remove border radius in all browsers.
|
||||
3. Remove background color in all browsers.
|
||||
4. Ensure consistent opacity for disabled states in all browsers.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
optgroup,
|
||||
textarea,
|
||||
::file-selector-button {
|
||||
font: inherit; /* 1 */
|
||||
font-feature-settings: inherit; /* 1 */
|
||||
font-variation-settings: inherit; /* 1 */
|
||||
letter-spacing: inherit; /* 1 */
|
||||
color: inherit; /* 1 */
|
||||
border-radius: 0; /* 2 */
|
||||
background-color: transparent; /* 3 */
|
||||
opacity: 1; /* 4 */
|
||||
}
|
||||
|
||||
/*
|
||||
Restore default font weight.
|
||||
*/
|
||||
|
||||
:where(select:is([multiple], [size])) optgroup {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/*
|
||||
Restore indentation.
|
||||
*/
|
||||
|
||||
:where(select:is([multiple], [size])) optgroup option {
|
||||
padding-inline-start: 20px;
|
||||
}
|
||||
|
||||
/*
|
||||
Restore space after button.
|
||||
*/
|
||||
|
||||
::file-selector-button {
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
|
||||
2. Set the default placeholder color to a semi-transparent version of the current text color.
|
||||
*/
|
||||
|
||||
::placeholder {
|
||||
opacity: 1; /* 1 */
|
||||
color: color-mix(in oklab, currentColor 50%, transparent); /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent resizing textareas horizontally by default.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Ensure date/time inputs have the same height when empty in iOS Safari.
|
||||
2. Ensure text alignment can be changed on date/time inputs in iOS Safari.
|
||||
*/
|
||||
|
||||
::-webkit-date-and-time-value {
|
||||
min-height: 1lh; /* 1 */
|
||||
text-align: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent height from changing on date/time inputs in macOS Safari when the input is set to `display: block`.
|
||||
*/
|
||||
|
||||
::-webkit-datetime-edit {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove excess padding from pseudo-elements in date/time inputs to ensure consistent height across browsers.
|
||||
*/
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit,
|
||||
::-webkit-datetime-edit-year-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-minute-field,
|
||||
::-webkit-datetime-edit-second-field,
|
||||
::-webkit-datetime-edit-millisecond-field,
|
||||
::-webkit-datetime-edit-meridiem-field {
|
||||
padding-block: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
|
||||
*/
|
||||
|
||||
:-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Correct the inability to style the border radius in iOS Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input:where([type="button"], [type="reset"], [type="submit"]),
|
||||
::file-selector-button {
|
||||
appearance: button;
|
||||
}
|
||||
|
||||
/*
|
||||
Correct the cursor style of increment and decrement buttons in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-inner-spin-button,
|
||||
::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Make elements with the HTML hidden attribute stay hidden by default.
|
||||
*/
|
||||
|
||||
[hidden]:where(:not([hidden="until-found"])) {
|
||||
display: none !important;
|
||||
}
|
||||
Reference in New Issue
Block a user