mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-19 07:04:21 +01:00
feat: non-editable bundled extensions (#2114)
This commit is contained in:
@@ -70,6 +70,7 @@ pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
|
|||||||
name: "developer".to_string(),
|
name: "developer".to_string(),
|
||||||
display_name: Some(goose::config::DEFAULT_DISPLAY_NAME.to_string()),
|
display_name: Some(goose::config::DEFAULT_DISPLAY_NAME.to_string()),
|
||||||
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
||||||
|
bundled: Some(true),
|
||||||
},
|
},
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@@ -509,6 +510,7 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
|
|||||||
name: extension.clone(),
|
name: extension.clone(),
|
||||||
display_name: Some(display_name),
|
display_name: Some(display_name),
|
||||||
timeout: Some(timeout),
|
timeout: Some(timeout),
|
||||||
|
bundled: Some(true),
|
||||||
},
|
},
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -600,6 +602,7 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
|
|||||||
envs: Envs::new(envs),
|
envs: Envs::new(envs),
|
||||||
description,
|
description,
|
||||||
timeout: Some(timeout),
|
timeout: Some(timeout),
|
||||||
|
bundled: None,
|
||||||
},
|
},
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -686,6 +689,7 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
|
|||||||
envs: Envs::new(envs),
|
envs: Envs::new(envs),
|
||||||
description,
|
description,
|
||||||
timeout: Some(timeout),
|
timeout: Some(timeout),
|
||||||
|
bundled: None,
|
||||||
},
|
},
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ impl Session {
|
|||||||
description: Some(goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string()),
|
description: Some(goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string()),
|
||||||
// TODO: should set timeout
|
// TODO: should set timeout
|
||||||
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
||||||
|
bundled: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.agent
|
self.agent
|
||||||
@@ -190,6 +191,7 @@ impl Session {
|
|||||||
description: Some(goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string()),
|
description: Some(goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string()),
|
||||||
// TODO: should set timeout
|
// TODO: should set timeout
|
||||||
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
||||||
|
bundled: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.agent
|
self.agent
|
||||||
@@ -214,6 +216,7 @@ impl Session {
|
|||||||
display_name: None,
|
display_name: None,
|
||||||
// TODO: should set a timeout
|
// TODO: should set a timeout
|
||||||
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
||||||
|
bundled: None,
|
||||||
};
|
};
|
||||||
self.agent
|
self.agent
|
||||||
.add_extension(config)
|
.add_extension(config)
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ async fn add_extension(
|
|||||||
envs: Envs::new(env_map),
|
envs: Envs::new(env_map),
|
||||||
description: None,
|
description: None,
|
||||||
timeout,
|
timeout,
|
||||||
|
bundled: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExtensionConfigRequest::Stdio {
|
ExtensionConfigRequest::Stdio {
|
||||||
@@ -254,6 +255,7 @@ async fn add_extension(
|
|||||||
description: None,
|
description: None,
|
||||||
envs: Envs::new(env_map),
|
envs: Envs::new(env_map),
|
||||||
timeout,
|
timeout,
|
||||||
|
bundled: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExtensionConfigRequest::Builtin {
|
ExtensionConfigRequest::Builtin {
|
||||||
@@ -264,6 +266,7 @@ async fn add_extension(
|
|||||||
name,
|
name,
|
||||||
display_name,
|
display_name,
|
||||||
timeout,
|
timeout,
|
||||||
|
bundled: None,
|
||||||
},
|
},
|
||||||
ExtensionConfigRequest::Frontend {
|
ExtensionConfigRequest::Frontend {
|
||||||
name,
|
name,
|
||||||
@@ -273,6 +276,7 @@ async fn add_extension(
|
|||||||
name,
|
name,
|
||||||
tools,
|
tools,
|
||||||
instructions,
|
instructions,
|
||||||
|
bundled: None,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ impl Agent {
|
|||||||
name: _,
|
name: _,
|
||||||
tools,
|
tools,
|
||||||
instructions,
|
instructions,
|
||||||
|
bundled: _,
|
||||||
} => {
|
} => {
|
||||||
// For frontend tools, just store them in the frontend_tools map
|
// For frontend tools, just store them in the frontend_tools map
|
||||||
for tool in tools {
|
for tool in tools {
|
||||||
|
|||||||
@@ -130,6 +130,9 @@ pub enum ExtensionConfig {
|
|||||||
// NOTE: set timeout to be optional for compatibility.
|
// NOTE: set timeout to be optional for compatibility.
|
||||||
// However, new configurations should include this field.
|
// However, new configurations should include this field.
|
||||||
timeout: Option<u64>,
|
timeout: Option<u64>,
|
||||||
|
/// Whether this extension is bundled with Goose
|
||||||
|
#[serde(default)]
|
||||||
|
bundled: Option<bool>,
|
||||||
},
|
},
|
||||||
/// Standard I/O client with command and arguments
|
/// Standard I/O client with command and arguments
|
||||||
#[serde(rename = "stdio")]
|
#[serde(rename = "stdio")]
|
||||||
@@ -142,6 +145,9 @@ pub enum ExtensionConfig {
|
|||||||
envs: Envs,
|
envs: Envs,
|
||||||
timeout: Option<u64>,
|
timeout: Option<u64>,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
|
/// Whether this extension is bundled with Goose
|
||||||
|
#[serde(default)]
|
||||||
|
bundled: Option<bool>,
|
||||||
},
|
},
|
||||||
/// Built-in extension that is part of the goose binary
|
/// Built-in extension that is part of the goose binary
|
||||||
#[serde(rename = "builtin")]
|
#[serde(rename = "builtin")]
|
||||||
@@ -150,6 +156,9 @@ pub enum ExtensionConfig {
|
|||||||
name: String,
|
name: String,
|
||||||
display_name: Option<String>, // needed for the UI
|
display_name: Option<String>, // needed for the UI
|
||||||
timeout: Option<u64>,
|
timeout: Option<u64>,
|
||||||
|
/// Whether this extension is bundled with Goose
|
||||||
|
#[serde(default)]
|
||||||
|
bundled: Option<bool>,
|
||||||
},
|
},
|
||||||
/// Frontend-provided tools that will be called through the frontend
|
/// Frontend-provided tools that will be called through the frontend
|
||||||
#[serde(rename = "frontend")]
|
#[serde(rename = "frontend")]
|
||||||
@@ -160,6 +169,9 @@ pub enum ExtensionConfig {
|
|||||||
tools: Vec<Tool>,
|
tools: Vec<Tool>,
|
||||||
/// Instructions for how to use these tools
|
/// Instructions for how to use these tools
|
||||||
instructions: Option<String>,
|
instructions: Option<String>,
|
||||||
|
/// Whether this extension is bundled with Goose
|
||||||
|
#[serde(default)]
|
||||||
|
bundled: Option<bool>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +181,7 @@ impl Default for ExtensionConfig {
|
|||||||
name: config::DEFAULT_EXTENSION.to_string(),
|
name: config::DEFAULT_EXTENSION.to_string(),
|
||||||
display_name: Some(config::DEFAULT_DISPLAY_NAME.to_string()),
|
display_name: Some(config::DEFAULT_DISPLAY_NAME.to_string()),
|
||||||
timeout: Some(config::DEFAULT_EXTENSION_TIMEOUT),
|
timeout: Some(config::DEFAULT_EXTENSION_TIMEOUT),
|
||||||
|
bundled: Some(true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,6 +194,7 @@ impl ExtensionConfig {
|
|||||||
envs: Envs::default(),
|
envs: Envs::default(),
|
||||||
description: Some(description.into()),
|
description: Some(description.into()),
|
||||||
timeout: Some(timeout.into()),
|
timeout: Some(timeout.into()),
|
||||||
|
bundled: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +211,7 @@ impl ExtensionConfig {
|
|||||||
envs: Envs::default(),
|
envs: Envs::default(),
|
||||||
description: Some(description.into()),
|
description: Some(description.into()),
|
||||||
timeout: Some(timeout.into()),
|
timeout: Some(timeout.into()),
|
||||||
|
bundled: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +227,7 @@ impl ExtensionConfig {
|
|||||||
envs,
|
envs,
|
||||||
timeout,
|
timeout,
|
||||||
description,
|
description,
|
||||||
|
bundled,
|
||||||
..
|
..
|
||||||
} => Self::Stdio {
|
} => Self::Stdio {
|
||||||
name,
|
name,
|
||||||
@@ -220,6 +236,7 @@ impl ExtensionConfig {
|
|||||||
args: args.into_iter().map(Into::into).collect(),
|
args: args.into_iter().map(Into::into).collect(),
|
||||||
description,
|
description,
|
||||||
timeout,
|
timeout,
|
||||||
|
bundled,
|
||||||
},
|
},
|
||||||
other => other,
|
other => other,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ impl ExtensionManager {
|
|||||||
name,
|
name,
|
||||||
display_name: _,
|
display_name: _,
|
||||||
timeout,
|
timeout,
|
||||||
|
bundled: _,
|
||||||
} => {
|
} => {
|
||||||
// For builtin extensions, we run the current executable with mcp and extension name
|
// For builtin extensions, we run the current executable with mcp and extension name
|
||||||
let cmd = std::env::current_exe()
|
let cmd = std::env::current_exe()
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ impl ExtensionConfigManager {
|
|||||||
name: DEFAULT_EXTENSION.to_string(),
|
name: DEFAULT_EXTENSION.to_string(),
|
||||||
display_name: Some(DEFAULT_DISPLAY_NAME.to_string()),
|
display_name: Some(DEFAULT_DISPLAY_NAME.to_string()),
|
||||||
timeout: Some(DEFAULT_EXTENSION_TIMEOUT),
|
timeout: Some(DEFAULT_EXTENSION_TIMEOUT),
|
||||||
|
bundled: Some(true),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)]);
|
)]);
|
||||||
|
|||||||
@@ -390,6 +390,11 @@
|
|||||||
"type"
|
"type"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"bundled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether this extension is bundled with Goose",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
@@ -434,6 +439,11 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"bundled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether this extension is bundled with Goose",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
"cmd": {
|
"cmd": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -470,6 +480,11 @@
|
|||||||
"type"
|
"type"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"bundled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether this extension is bundled with Goose",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
"display_name": {
|
"display_name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
@@ -501,6 +516,11 @@
|
|||||||
"type"
|
"type"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"bundled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether this extension is bundled with Goose",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Instructions for how to use these tools",
|
"description": "Instructions for how to use these tools",
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ export type Envs = {
|
|||||||
* Represents the different types of MCP extensions that can be added to the manager
|
* Represents the different types of MCP extensions that can be added to the manager
|
||||||
*/
|
*/
|
||||||
export type ExtensionConfig = {
|
export type ExtensionConfig = {
|
||||||
|
/**
|
||||||
|
* Whether this extension is bundled with Goose
|
||||||
|
*/
|
||||||
|
bundled?: boolean | null;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
envs?: Envs;
|
envs?: Envs;
|
||||||
/**
|
/**
|
||||||
@@ -35,6 +39,10 @@ export type ExtensionConfig = {
|
|||||||
uri: string;
|
uri: string;
|
||||||
} | {
|
} | {
|
||||||
args: Array<string>;
|
args: Array<string>;
|
||||||
|
/**
|
||||||
|
* Whether this extension is bundled with Goose
|
||||||
|
*/
|
||||||
|
bundled?: boolean | null;
|
||||||
cmd: string;
|
cmd: string;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
envs?: Envs;
|
envs?: Envs;
|
||||||
@@ -45,6 +53,10 @@ export type ExtensionConfig = {
|
|||||||
timeout?: number | null;
|
timeout?: number | null;
|
||||||
type: 'stdio';
|
type: 'stdio';
|
||||||
} | {
|
} | {
|
||||||
|
/**
|
||||||
|
* Whether this extension is bundled with Goose
|
||||||
|
*/
|
||||||
|
bundled?: boolean | null;
|
||||||
display_name?: string | null;
|
display_name?: string | null;
|
||||||
/**
|
/**
|
||||||
* The name used to identify this extension
|
* The name used to identify this extension
|
||||||
@@ -53,6 +65,10 @@ export type ExtensionConfig = {
|
|||||||
timeout?: number | null;
|
timeout?: number | null;
|
||||||
type: 'builtin';
|
type: 'builtin';
|
||||||
} | {
|
} | {
|
||||||
|
/**
|
||||||
|
* Whether this extension is bundled with Goose
|
||||||
|
*/
|
||||||
|
bundled?: boolean | null;
|
||||||
/**
|
/**
|
||||||
* Instructions for how to use these tools
|
* Instructions for how to use these tools
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"type": "builtin",
|
"type": "builtin",
|
||||||
"env_keys": [],
|
"env_keys": [],
|
||||||
"timeout": 300
|
"timeout": 300,
|
||||||
|
"bundled": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "computercontroller",
|
"id": "computercontroller",
|
||||||
@@ -17,7 +18,8 @@
|
|||||||
"enabled": false,
|
"enabled": false,
|
||||||
"type": "builtin",
|
"type": "builtin",
|
||||||
"env_keys": [],
|
"env_keys": [],
|
||||||
"timeout": 300
|
"timeout": 300,
|
||||||
|
"bundled": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "memory",
|
"id": "memory",
|
||||||
@@ -27,7 +29,8 @@
|
|||||||
"enabled": false,
|
"enabled": false,
|
||||||
"type": "builtin",
|
"type": "builtin",
|
||||||
"env_keys": [],
|
"env_keys": [],
|
||||||
"timeout": 300
|
"timeout": 300,
|
||||||
|
"bundled": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "jetbrains",
|
"id": "jetbrains",
|
||||||
@@ -37,7 +40,8 @@
|
|||||||
"enabled": false,
|
"enabled": false,
|
||||||
"type": "builtin",
|
"type": "builtin",
|
||||||
"env_keys": [],
|
"env_keys": [],
|
||||||
"timeout": 300
|
"timeout": 300,
|
||||||
|
"bundled": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "tutorial",
|
"id": "tutorial",
|
||||||
@@ -46,6 +50,7 @@
|
|||||||
"description": "Access interactive tutorials and guides",
|
"description": "Access interactive tutorials and guides",
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"type": "builtin",
|
"type": "builtin",
|
||||||
"env_keys": []
|
"env_keys": [],
|
||||||
|
"bundled": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -33,20 +33,18 @@ export async function syncBundledExtensions(
|
|||||||
addExtensionFn: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>
|
addExtensionFn: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// Create a set of existing extension IDs for quick lookup
|
|
||||||
const existingExtensionKeys = new Set(existingExtensions.map((ext) => nameToKey(ext.name)));
|
|
||||||
|
|
||||||
// Cast the imported JSON data to the expected type
|
// Cast the imported JSON data to the expected type
|
||||||
const bundledExtensions = bundledExtensionsData as BundledExtension[];
|
const bundledExtensions = bundledExtensionsData as BundledExtension[];
|
||||||
|
|
||||||
// Track how many extensions were added
|
// Process each bundled extension
|
||||||
let addedCount = 0;
|
|
||||||
|
|
||||||
// Check each built-in extension
|
|
||||||
for (const bundledExt of bundledExtensions) {
|
for (const bundledExt of bundledExtensions) {
|
||||||
// Only add if the extension doesn't already exist -- use the id
|
// Find if this extension already exists
|
||||||
if (!existingExtensionKeys.has(bundledExt.id)) {
|
const existingExt = existingExtensions.find((ext) => nameToKey(ext.name) === bundledExt.id);
|
||||||
console.log(`Adding built-in extension: ${bundledExt.id}`);
|
|
||||||
|
// Skip if extension exists and is already marked as bundled
|
||||||
|
if (existingExt?.bundled) continue;
|
||||||
|
|
||||||
|
// Create the config for this extension
|
||||||
let extConfig: ExtensionConfig;
|
let extConfig: ExtensionConfig;
|
||||||
switch (bundledExt.type) {
|
switch (bundledExt.type) {
|
||||||
case 'builtin':
|
case 'builtin':
|
||||||
@@ -55,6 +53,7 @@ export async function syncBundledExtensions(
|
|||||||
display_name: bundledExt.display_name,
|
display_name: bundledExt.display_name,
|
||||||
type: bundledExt.type,
|
type: bundledExt.type,
|
||||||
timeout: bundledExt.timeout ?? 300,
|
timeout: bundledExt.timeout ?? 300,
|
||||||
|
bundled: true,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case 'stdio':
|
case 'stdio':
|
||||||
@@ -66,6 +65,7 @@ export async function syncBundledExtensions(
|
|||||||
cmd: bundledExt.cmd,
|
cmd: bundledExt.cmd,
|
||||||
args: bundledExt.args,
|
args: bundledExt.args,
|
||||||
envs: bundledExt.envs,
|
envs: bundledExt.envs,
|
||||||
|
bundled: true,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case 'sse':
|
case 'sse':
|
||||||
@@ -75,26 +75,16 @@ export async function syncBundledExtensions(
|
|||||||
type: bundledExt.type,
|
type: bundledExt.type,
|
||||||
timeout: bundledExt.timeout,
|
timeout: bundledExt.timeout,
|
||||||
uri: bundledExt.uri,
|
uri: bundledExt.uri,
|
||||||
|
bundled: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Add the extension with its default enabled state
|
|
||||||
try {
|
|
||||||
await addExtensionFn(bundledExt.name, extConfig, bundledExt.enabled);
|
|
||||||
addedCount++;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to add built-in extension ${bundledExt.name}:`, error);
|
|
||||||
// Continue with other extensions even if one fails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addedCount > 0) {
|
// Add or update the extension, preserving enabled state if it exists
|
||||||
console.log(`Added ${addedCount} built-in extensions.`);
|
const enabled = existingExt ? existingExt.enabled : bundledExt.enabled;
|
||||||
} else {
|
await addExtensionFn(bundledExt.name, extConfig, enabled);
|
||||||
console.log('All built-in extensions already present.');
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to add built-in extensions:', error);
|
console.error('Failed to sync built-in extensions:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ export default function ExtensionItem({ extension, onToggle, onConfigure }: Exte
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Bundled extensions and builtins are not editable
|
||||||
|
// Over time we can take the first part of the conditional away as people have bundled: true in their config.yaml entries
|
||||||
|
const editable = !(extension.type === 'builtin' || extension.bundled);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex justify-between rounded-lg transition-colors border border-borderSubtle p-4 pt-3 hover:border-borderProminent hover:cursor-pointer"
|
className="flex justify-between rounded-lg transition-colors border border-borderSubtle p-4 pt-3 hover:border-borderProminent hover:cursor-pointer"
|
||||||
@@ -70,8 +74,7 @@ export default function ExtensionItem({ extension, onToggle, onConfigure }: Exte
|
|||||||
className="flex items-center justify-end gap-2 w-max-[10%]"
|
className="flex items-center justify-end gap-2 w-max-[10%]"
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
{/* Only show config button for non-builtin extensions */}
|
{editable && (
|
||||||
{extension.type !== 'builtin' && (
|
|
||||||
<button
|
<button
|
||||||
className="text-textSubtle hover:text-textStandard"
|
className="text-textSubtle hover:text-textStandard"
|
||||||
onClick={() => onConfigure(extension)}
|
onClick={() => onConfigure(extension)}
|
||||||
|
|||||||
Reference in New Issue
Block a user