|
|
|
|
@@ -6,6 +6,9 @@ import { generateApiKey } from './utils/auth-utils';
|
|
|
|
|
export class MCPServerSettingTab extends PluginSettingTab {
|
|
|
|
|
plugin: MCPServerPlugin;
|
|
|
|
|
private notificationDetailsEl: HTMLDetailsElement | null = null;
|
|
|
|
|
private notificationToggleEl: HTMLElement | null = null;
|
|
|
|
|
private authDetailsEl: HTMLDetailsElement | null = null;
|
|
|
|
|
private configContainerEl: HTMLElement | null = null;
|
|
|
|
|
private activeConfigTab: 'windsurf' | 'claude-code' = 'windsurf';
|
|
|
|
|
|
|
|
|
|
constructor(app: App, plugin: MCPServerPlugin) {
|
|
|
|
|
@@ -118,8 +121,11 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|
|
|
|
|
|
|
|
|
containerEl.empty();
|
|
|
|
|
|
|
|
|
|
// Clear notification details reference for fresh render
|
|
|
|
|
// Clear references for fresh render
|
|
|
|
|
this.notificationDetailsEl = null;
|
|
|
|
|
this.notificationToggleEl = null;
|
|
|
|
|
this.authDetailsEl = null;
|
|
|
|
|
this.configContainerEl = null;
|
|
|
|
|
|
|
|
|
|
containerEl.createEl('h2', {text: 'MCP Server Settings'});
|
|
|
|
|
|
|
|
|
|
@@ -198,6 +204,9 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|
|
|
|
authSummary.style.cursor = 'pointer';
|
|
|
|
|
authSummary.setText('Authentication & Configuration');
|
|
|
|
|
|
|
|
|
|
// Store reference for targeted updates
|
|
|
|
|
this.authDetailsEl = authDetails;
|
|
|
|
|
|
|
|
|
|
// API Key Display (always show - auth is always enabled)
|
|
|
|
|
new Setting(authDetails)
|
|
|
|
|
.setName('API Key Management')
|
|
|
|
|
@@ -254,6 +263,9 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|
|
|
|
const configContainer = authDetails.createDiv({cls: 'mcp-config-snippet'});
|
|
|
|
|
configContainer.style.marginBottom = '20px';
|
|
|
|
|
|
|
|
|
|
// Store reference for targeted updates
|
|
|
|
|
this.configContainerEl = configContainer;
|
|
|
|
|
|
|
|
|
|
// Tab buttons for switching between clients
|
|
|
|
|
const tabContainer = configContainer.createDiv({cls: 'mcp-config-tabs'});
|
|
|
|
|
tabContainer.style.display = 'flex';
|
|
|
|
|
@@ -273,7 +285,7 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|
|
|
|
windsurfTab.style.fontWeight = this.activeConfigTab === 'windsurf' ? 'bold' : 'normal';
|
|
|
|
|
windsurfTab.addEventListener('click', () => {
|
|
|
|
|
this.activeConfigTab = 'windsurf';
|
|
|
|
|
this.display();
|
|
|
|
|
this.updateConfigSection();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Claude Code tab button
|
|
|
|
|
@@ -288,7 +300,7 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|
|
|
|
claudeCodeTab.style.fontWeight = this.activeConfigTab === 'claude-code' ? 'bold' : 'normal';
|
|
|
|
|
claudeCodeTab.addEventListener('click', () => {
|
|
|
|
|
this.activeConfigTab = 'claude-code';
|
|
|
|
|
this.display();
|
|
|
|
|
this.updateConfigSection();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Get configuration for active tab
|
|
|
|
|
@@ -353,8 +365,11 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|
|
|
|
// Store reference for targeted updates
|
|
|
|
|
this.notificationDetailsEl = notifDetails;
|
|
|
|
|
|
|
|
|
|
// Enable notifications
|
|
|
|
|
new Setting(notifDetails)
|
|
|
|
|
// Enable notifications - create container for the toggle setting
|
|
|
|
|
const notificationToggleContainer = notifDetails.createDiv({cls: 'mcp-notification-toggle'});
|
|
|
|
|
this.notificationToggleEl = notificationToggleContainer;
|
|
|
|
|
|
|
|
|
|
new Setting(notificationToggleContainer)
|
|
|
|
|
.setName('Enable notifications')
|
|
|
|
|
.setDesc('Show when MCP tools are called')
|
|
|
|
|
.addToggle(toggle => toggle
|
|
|
|
|
@@ -376,7 +391,7 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|
|
|
|
* Update only the notification section without re-rendering entire page
|
|
|
|
|
*/
|
|
|
|
|
private updateNotificationSection(): void {
|
|
|
|
|
if (!this.notificationDetailsEl) {
|
|
|
|
|
if (!this.notificationDetailsEl || !this.notificationToggleEl) {
|
|
|
|
|
// Fallback to full re-render if reference lost
|
|
|
|
|
this.display();
|
|
|
|
|
return;
|
|
|
|
|
@@ -385,13 +400,16 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|
|
|
|
// Store current open state
|
|
|
|
|
const wasOpen = this.notificationDetailsEl.open;
|
|
|
|
|
|
|
|
|
|
// Find and remove all child elements except the summary
|
|
|
|
|
// Remove all children except the summary and the toggle container
|
|
|
|
|
const summary = this.notificationDetailsEl.querySelector('summary');
|
|
|
|
|
while (this.notificationDetailsEl.lastChild && this.notificationDetailsEl.lastChild !== summary) {
|
|
|
|
|
this.notificationDetailsEl.removeChild(this.notificationDetailsEl.lastChild);
|
|
|
|
|
const children = Array.from(this.notificationDetailsEl.children);
|
|
|
|
|
for (const child of children) {
|
|
|
|
|
if (child !== summary && child !== this.notificationToggleEl) {
|
|
|
|
|
this.notificationDetailsEl.removeChild(child);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rebuild notification settings
|
|
|
|
|
// Rebuild notification settings only if enabled
|
|
|
|
|
if (this.plugin.settings.notificationsEnabled) {
|
|
|
|
|
this.renderNotificationSettings(this.notificationDetailsEl);
|
|
|
|
|
}
|
|
|
|
|
@@ -399,4 +417,112 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|
|
|
|
// Restore open state
|
|
|
|
|
this.notificationDetailsEl.open = wasOpen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update only the config section without re-rendering entire page
|
|
|
|
|
*/
|
|
|
|
|
private updateConfigSection(): void {
|
|
|
|
|
if (!this.configContainerEl) {
|
|
|
|
|
// Fallback to full re-render if reference lost
|
|
|
|
|
this.display();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store current open state of the auth details
|
|
|
|
|
const wasOpen = this.authDetailsEl?.open ?? false;
|
|
|
|
|
|
|
|
|
|
// Clear the config container
|
|
|
|
|
this.configContainerEl.empty();
|
|
|
|
|
|
|
|
|
|
// Tab buttons for switching between clients
|
|
|
|
|
const tabContainer = this.configContainerEl.createDiv({cls: 'mcp-config-tabs'});
|
|
|
|
|
tabContainer.style.display = 'flex';
|
|
|
|
|
tabContainer.style.gap = '8px';
|
|
|
|
|
tabContainer.style.marginBottom = '16px';
|
|
|
|
|
tabContainer.style.borderBottom = '1px solid var(--background-modifier-border)';
|
|
|
|
|
|
|
|
|
|
// Windsurf tab button
|
|
|
|
|
const windsurfTab = tabContainer.createEl('button', {text: 'Windsurf'});
|
|
|
|
|
windsurfTab.style.padding = '8px 16px';
|
|
|
|
|
windsurfTab.style.border = 'none';
|
|
|
|
|
windsurfTab.style.background = 'none';
|
|
|
|
|
windsurfTab.style.cursor = 'pointer';
|
|
|
|
|
windsurfTab.style.borderBottom = this.activeConfigTab === 'windsurf'
|
|
|
|
|
? '2px solid var(--interactive-accent)'
|
|
|
|
|
: '2px solid transparent';
|
|
|
|
|
windsurfTab.style.fontWeight = this.activeConfigTab === 'windsurf' ? 'bold' : 'normal';
|
|
|
|
|
windsurfTab.addEventListener('click', () => {
|
|
|
|
|
this.activeConfigTab = 'windsurf';
|
|
|
|
|
this.updateConfigSection();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Claude Code tab button
|
|
|
|
|
const claudeCodeTab = tabContainer.createEl('button', {text: 'Claude Code'});
|
|
|
|
|
claudeCodeTab.style.padding = '8px 16px';
|
|
|
|
|
claudeCodeTab.style.border = 'none';
|
|
|
|
|
claudeCodeTab.style.background = 'none';
|
|
|
|
|
claudeCodeTab.style.cursor = 'pointer';
|
|
|
|
|
claudeCodeTab.style.borderBottom = this.activeConfigTab === 'claude-code'
|
|
|
|
|
? '2px solid var(--interactive-accent)'
|
|
|
|
|
: '2px solid transparent';
|
|
|
|
|
claudeCodeTab.style.fontWeight = this.activeConfigTab === 'claude-code' ? 'bold' : 'normal';
|
|
|
|
|
claudeCodeTab.addEventListener('click', () => {
|
|
|
|
|
this.activeConfigTab = 'claude-code';
|
|
|
|
|
this.updateConfigSection();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Get configuration for active tab
|
|
|
|
|
const {filePath, config, usageNote} = this.generateConfigForClient(this.activeConfigTab);
|
|
|
|
|
|
|
|
|
|
// Tab content area
|
|
|
|
|
const tabContent = this.configContainerEl.createDiv({cls: 'mcp-config-content'});
|
|
|
|
|
tabContent.style.marginTop = '16px';
|
|
|
|
|
|
|
|
|
|
// File location label
|
|
|
|
|
const fileLocationLabel = tabContent.createEl('p', {text: 'Configuration file location:'});
|
|
|
|
|
fileLocationLabel.style.marginBottom = '4px';
|
|
|
|
|
fileLocationLabel.style.fontSize = '0.9em';
|
|
|
|
|
fileLocationLabel.style.color = 'var(--text-muted)';
|
|
|
|
|
|
|
|
|
|
// File path display
|
|
|
|
|
const filePathDisplay = tabContent.createEl('div', {text: filePath});
|
|
|
|
|
filePathDisplay.style.padding = '8px';
|
|
|
|
|
filePathDisplay.style.backgroundColor = 'var(--background-secondary)';
|
|
|
|
|
filePathDisplay.style.borderRadius = '4px';
|
|
|
|
|
filePathDisplay.style.fontFamily = 'monospace';
|
|
|
|
|
filePathDisplay.style.fontSize = '0.9em';
|
|
|
|
|
filePathDisplay.style.marginBottom = '12px';
|
|
|
|
|
filePathDisplay.style.color = 'var(--text-muted)';
|
|
|
|
|
|
|
|
|
|
// Copy button
|
|
|
|
|
const copyConfigButton = tabContent.createEl('button', {text: '📋 Copy Configuration'});
|
|
|
|
|
copyConfigButton.style.marginBottom = '12px';
|
|
|
|
|
copyConfigButton.addEventListener('click', async () => {
|
|
|
|
|
await navigator.clipboard.writeText(JSON.stringify(config, null, 2));
|
|
|
|
|
new Notice('✅ Configuration copied to clipboard');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Config JSON display
|
|
|
|
|
const configDisplay = tabContent.createEl('pre');
|
|
|
|
|
configDisplay.style.padding = '12px';
|
|
|
|
|
configDisplay.style.backgroundColor = 'var(--background-secondary)';
|
|
|
|
|
configDisplay.style.borderRadius = '4px';
|
|
|
|
|
configDisplay.style.fontSize = '0.85em';
|
|
|
|
|
configDisplay.style.overflowX = 'auto';
|
|
|
|
|
configDisplay.style.userSelect = 'text';
|
|
|
|
|
configDisplay.style.cursor = 'text';
|
|
|
|
|
configDisplay.style.marginBottom = '12px';
|
|
|
|
|
configDisplay.textContent = JSON.stringify(config, null, 2);
|
|
|
|
|
|
|
|
|
|
// Usage note
|
|
|
|
|
const usageNoteDisplay = tabContent.createEl('p', {text: usageNote});
|
|
|
|
|
usageNoteDisplay.style.fontSize = '0.9em';
|
|
|
|
|
usageNoteDisplay.style.color = 'var(--text-muted)';
|
|
|
|
|
usageNoteDisplay.style.fontStyle = 'italic';
|
|
|
|
|
|
|
|
|
|
// Restore open state (only if authDetailsEl is available)
|
|
|
|
|
if (this.authDetailsEl) {
|
|
|
|
|
this.authDetailsEl.open = wasOpen;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|