@memberjunction/ng-dashboards 5.44.0 → 5.45.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts +1 -0
- package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js +5 -3
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js.map +1 -1
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.d.ts +4 -2
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.d.ts.map +1 -1
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js +13 -11
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-version-control.component.d.ts +3 -1
- package/dist/AI/components/prompts/prompt-version-control.component.d.ts.map +1 -1
- package/dist/AI/components/prompts/prompt-version-control.component.js +12 -10
- package/dist/AI/components/prompts/prompt-version-control.component.js.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.d.ts +1 -0
- package/dist/AI/components/tags/tags-resource.component.d.ts.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.js +10 -8
- package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
- package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.d.ts +2 -1
- package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js +1 -1
- package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js.map +1 -1
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.d.ts +1 -0
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js +5 -3
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js.map +1 -1
- package/dist/Credentials/components/credentials-categories-resource.component.d.ts +3 -1
- package/dist/Credentials/components/credentials-categories-resource.component.d.ts.map +1 -1
- package/dist/Credentials/components/credentials-categories-resource.component.js +11 -5
- package/dist/Credentials/components/credentials-categories-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.d.ts +3 -2
- package/dist/Credentials/components/credentials-list-resource.component.d.ts.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.js +23 -9
- package/dist/Credentials/components/credentials-list-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-types-resource.component.d.ts +3 -2
- package/dist/Credentials/components/credentials-types-resource.component.d.ts.map +1 -1
- package/dist/Credentials/components/credentials-types-resource.component.js +10 -4
- package/dist/Credentials/components/credentials-types-resource.component.js.map +1 -1
- package/dist/DatabaseDesigner/components/modify/database-modify.component.d.ts +2 -1
- package/dist/DatabaseDesigner/components/modify/database-modify.component.d.ts.map +1 -1
- package/dist/DatabaseDesigner/components/modify/database-modify.component.js +13 -6
- package/dist/DatabaseDesigner/components/modify/database-modify.component.js.map +1 -1
- package/dist/FormBuilder/form-builder-resource.component.d.ts +2 -1
- package/dist/FormBuilder/form-builder-resource.component.d.ts.map +1 -1
- package/dist/FormBuilder/form-builder-resource.component.js +10 -8
- package/dist/FormBuilder/form-builder-resource.component.js.map +1 -1
- package/dist/MCP/mcp-dashboard.component.d.ts +3 -1
- package/dist/MCP/mcp-dashboard.component.d.ts.map +1 -1
- package/dist/MCP/mcp-dashboard.component.js +32 -14
- package/dist/MCP/mcp-dashboard.component.js.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.d.ts +4 -2
- package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js +15 -13
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/UserRoutines/user-routines-agent-context.d.ts +44 -0
- package/dist/UserRoutines/user-routines-agent-context.d.ts.map +1 -0
- package/dist/UserRoutines/user-routines-agent-context.js +81 -0
- package/dist/UserRoutines/user-routines-agent-context.js.map +1 -0
- package/dist/UserRoutines/user-routines-resource.component.d.ts +60 -0
- package/dist/UserRoutines/user-routines-resource.component.d.ts.map +1 -0
- package/dist/UserRoutines/user-routines-resource.component.js +328 -0
- package/dist/UserRoutines/user-routines-resource.component.js.map +1 -0
- package/dist/module.d.ts +16 -15
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +7 -0
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +2 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +3 -0
- package/dist/public-api.js.map +1 -1
- package/dist/routines-dashboards.module.d.ts +17 -0
- package/dist/routines-dashboards.module.d.ts.map +1 -0
- package/dist/routines-dashboards.module.js +65 -0
- package/dist/routines-dashboards.module.js.map +1 -0
- package/package.json +63 -58
|
@@ -27,9 +27,9 @@ import { buildMCPAgentContextFull, resolveMCPItem, buildMCPNotFoundError, MCP_TA
|
|
|
27
27
|
import { validateEnumParam, validateStringParam } from '../shared/agent-tool-validation';
|
|
28
28
|
import * as i0 from "@angular/core";
|
|
29
29
|
import * as i1 from "./services/mcp-tools.service";
|
|
30
|
-
import * as i2 from "@
|
|
31
|
-
import * as i3 from "@angular/
|
|
32
|
-
import * as i4 from "@
|
|
30
|
+
import * as i2 from "@memberjunction/ng-ui-components";
|
|
31
|
+
import * as i3 from "@angular/common";
|
|
32
|
+
import * as i4 from "@angular/forms";
|
|
33
33
|
import * as i5 from "@memberjunction/ng-shared-generic";
|
|
34
34
|
const _forTrack0 = ($index, $item) => $item.ID;
|
|
35
35
|
const _forTrack1 = ($index, $item) => $item.server.ID;
|
|
@@ -1945,6 +1945,7 @@ function MCPDashboardComponent_Conditional_21_Template(rf, ctx) { if (rf & 1) {
|
|
|
1945
1945
|
let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
|
|
1946
1946
|
cdr;
|
|
1947
1947
|
mcpToolsService;
|
|
1948
|
+
confirm;
|
|
1948
1949
|
// ========================================
|
|
1949
1950
|
// State
|
|
1950
1951
|
// ========================================
|
|
@@ -2090,10 +2091,11 @@ let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
|
|
|
2090
2091
|
// Lifecycle
|
|
2091
2092
|
// ========================================
|
|
2092
2093
|
destroy$ = new Subject();
|
|
2093
|
-
constructor(cdr, mcpToolsService) {
|
|
2094
|
+
constructor(cdr, mcpToolsService, confirm) {
|
|
2094
2095
|
super();
|
|
2095
2096
|
this.cdr = cdr;
|
|
2096
2097
|
this.mcpToolsService = mcpToolsService;
|
|
2098
|
+
this.confirm = confirm;
|
|
2097
2099
|
// Set up debounced settings persistence
|
|
2098
2100
|
this.settingsPersistSubject.pipe(debounceTime(500), takeUntil(this.destroy$)).subscribe(() => {
|
|
2099
2101
|
this.persistUserPreferences();
|
|
@@ -3577,8 +3579,12 @@ let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
|
|
|
3577
3579
|
if (relatedTools.length > 0) {
|
|
3578
3580
|
parts.push(`${relatedTools.length} tool(s)`);
|
|
3579
3581
|
}
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
+
const confirmed = await this.confirm.ConfirmDelete({
|
|
3583
|
+
title: 'Delete server',
|
|
3584
|
+
message: `Delete server "${server.Name}"? It has ${parts.join(' and ')}.`,
|
|
3585
|
+
detail: 'All related records will be deleted. This cannot be undone.',
|
|
3586
|
+
});
|
|
3587
|
+
if (!confirmed) {
|
|
3582
3588
|
return;
|
|
3583
3589
|
}
|
|
3584
3590
|
// Delete related connections first (which will cascade to connection tools/permissions)
|
|
@@ -3605,7 +3611,11 @@ let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
|
|
|
3605
3611
|
}
|
|
3606
3612
|
}
|
|
3607
3613
|
else {
|
|
3608
|
-
|
|
3614
|
+
const confirmed = await this.confirm.ConfirmDelete({
|
|
3615
|
+
title: 'Delete server',
|
|
3616
|
+
message: `Delete server "${server.Name}"?`,
|
|
3617
|
+
});
|
|
3618
|
+
if (!confirmed) {
|
|
3609
3619
|
return;
|
|
3610
3620
|
}
|
|
3611
3621
|
}
|
|
@@ -3901,13 +3911,21 @@ let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
|
|
|
3901
3911
|
// Check for related execution logs
|
|
3902
3912
|
const relatedLogs = this.executionLogs.filter(l => UUIDsEqual(l.ConnectionID, connection.ID));
|
|
3903
3913
|
if (relatedLogs.length > 0) {
|
|
3904
|
-
|
|
3905
|
-
|
|
3914
|
+
const confirmed = await this.confirm.ConfirmDelete({
|
|
3915
|
+
title: 'Delete connection',
|
|
3916
|
+
message: `Delete connection "${connection.Name}"? It has ${relatedLogs.length} execution log(s).`,
|
|
3917
|
+
detail: 'All related logs will be deleted. This cannot be undone.',
|
|
3918
|
+
});
|
|
3919
|
+
if (!confirmed) {
|
|
3906
3920
|
return;
|
|
3907
3921
|
}
|
|
3908
3922
|
}
|
|
3909
3923
|
else {
|
|
3910
|
-
|
|
3924
|
+
const confirmed = await this.confirm.ConfirmDelete({
|
|
3925
|
+
title: 'Delete connection',
|
|
3926
|
+
message: `Delete connection "${connection.Name}"?`,
|
|
3927
|
+
});
|
|
3928
|
+
if (!confirmed) {
|
|
3911
3929
|
return;
|
|
3912
3930
|
}
|
|
3913
3931
|
}
|
|
@@ -5002,7 +5020,7 @@ let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
|
|
|
5002
5020
|
this.ShowTestToolDialog = true;
|
|
5003
5021
|
this.cdr.detectChanges();
|
|
5004
5022
|
}
|
|
5005
|
-
static ɵfac = function MCPDashboardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MCPDashboardComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.MCPToolsService)); };
|
|
5023
|
+
static ɵfac = function MCPDashboardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MCPDashboardComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.MCPToolsService), i0.ɵɵdirectiveInject(i2.MJConfirmService)); };
|
|
5006
5024
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MCPDashboardComponent, selectors: [["mj-mcp-dashboard"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 22, vars: 17, consts: [["sfName", ""], ["sfDesc", ""], ["sfTransport", ""], ["sfAuth", ""], ["sfStatus", ""], ["sfUrl", ""], ["sfCmd", ""], ["cfServer", ""], ["cfName", ""], ["cfToken", ""], ["cfStatus", ""], ["tSrv", ""], ["tConn", ""], ["tSearch", ""], ["enumEl", ""], ["boolEl", ""], ["jsonEl", ""], ["strEl", ""], ["Title", "MCP Dashboard", "Icon", "fa-solid fa-plug-circle-bolt"], ["meta", ""], ["Label", "items", 3, "Count", "Total"], ["actions", ""], ["mjButton", "", "variant", "primary", "size", "sm"], [3, "Loading"], ["toolbar", ""], [3, "TabChange", "Tabs", "ActiveKey"], ["Placeholder", "Search\u2026", 3, "ValueChange", "Value"], [3, "ClearAllRequested", "ActiveCount", "ShowClearAll"], [3, "ValuesChange", "Reset", "Fields", "Values"], [3, "Flex"], ["Variant", "error", "Dismissible", ""], ["text", "Loading MCP data..."], [2, "position", "fixed", "inset", "0", "z-index", "10000", "background", "rgba(0,0,0,0.5)", "display", "flex", "align-items", "center", "justify-content", "center"], [2, "position", "fixed", "inset", "0", "z-index", "10000", "pointer-events", "none"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "action-btn-label"], [3, "KeyChange", "Options", "ActiveKey"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click", "title"], [1, "fa-solid"], [1, "fa-solid", "fa-play"], [3, "Clicked", "Loading"], ["Variant", "error", "Dismissible", "", 3, "Dismissed"], [1, "data-grid", "servers-grid"], [1, "data-grid", "connections-grid"], [1, "data-table"], ["Icon", "fa-solid fa-server", "Title", "No MCP servers configured", "ActionText", "Add Your First Server"], ["Icon", "fa-solid fa-server", "Title", "No MCP servers configured", "ActionText", "Add Your First Server", 3, "Action"], [1, "data-card", 3, "expanded"], [1, "data-card"], [1, "card-header", "clickable", 3, "click"], [1, "card-title"], [1, "fa-solid", "fa-chevron-right", "expand-arrow"], [1, "name"], [1, "status-badge", 3, "ngClass"], [1, "card-actions", 3, "click"], ["mjButton", "", "variant", "flat", "title", "Edit", 3, "click"], [1, "fa-solid", "fa-pen"], ["mjButton", "", "variant", "flat", "title", "Delete", 3, "click"], [1, "fa-solid", "fa-trash"], [1, "card-body"], [1, "description"], [1, "details-grid"], [1, "detail"], [1, "label"], [1, "value"], [1, "detail", "full-width"], [1, "expanded-tools-section"], [1, "value", "url"], [1, "value", "command"], [1, "tools-section-header"], [1, "fa-solid", "fa-wrench"], [1, "no-tools-message"], [1, "tools-mini-list"], [1, "fa-solid", "fa-info-circle"], [1, "tool-mini-card"], [1, "tool-mini-info"], [1, "tool-mini-name"], [1, "tool-mini-params"], [1, "fa-solid", "fa-sliders"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Test this tool", 3, "click"], ["Icon", "fa-solid fa-link", "Title", "No connections configured", "ActionText", "Add Your First Connection"], ["Icon", "fa-solid fa-link", "Title", "No connections configured", "ActionText", "Add Your First Connection", 3, "Action"], [1, "fa-solid", "fa-link"], ["mjButton", "", "variant", "flat", "title", "Sync Tools", 3, "click", "disabled"], [1, "fa-solid", "fa-sync", "fa-spin"], [1, "fa-solid", "fa-sync"], [1, "sync-progress-banner"], ["Size", "sm", 3, "Variant"], [1, "detail", "full-width", "error"], [1, "fa-solid", "fa-circle-notch", "fa-spin"], [1, "sync-message"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Test this tool with this connection", 3, "click"], ["Icon", "fa-solid fa-wrench", "Title", "No tools discovered yet", "Message", "Tools are discovered when connections sync with MCP servers"], [1, "tools-container"], [2, "padding", "8px 12px", "font-size", "0.8rem", "color", "var(--mj-text-secondary,#475569)", "background", "var(--mj-bg-surface-card,#f8f9fa)", "border-radius", "4px", "margin-bottom", "6px"], [2, "height", "calc(100vh - 260px)", "width", "100%", "overflow-y", "auto", "border", "1px solid var(--mj-border-subtle,#eee)", "border-radius", "4px", 3, "scroll"], [2, "display", "flex", "align-items", "center", "gap", "12px", "padding", "12px 14px", "border-bottom", "1px solid var(--mj-border-subtle,#eee)", "height", "72px", "box-sizing", "border-box"], [2, "padding", "12px", "text-align", "center", "font-size", "0.8rem", "color", "var(--mj-text-muted,#888)"], [1, "fa-solid", "fa-wrench", 2, "color", "var(--mj-brand-primary,#264FAF)"], [2, "flex", "1", "min-width", "0"], [2, "font-weight", "500", "font-size", "0.875rem", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], ["type", "button", 2, "background", "transparent", "border", "none", "cursor", "pointer", "padding", "4px", "font-size", "1rem", 3, "click", "title"], [1, "status-badge", "small", 3, "ngClass"], [1, "server-group", 3, "collapsed"], [1, "server-group"], [1, "server-group-header", 3, "click"], [1, "server-info"], [1, "fa-solid", "fa-chevron-right", "expand-icon"], [1, "server-name"], [1, "tool-count"], [1, "server-actions", 3, "click"], ["mjButton", "", "variant", "flat", "title", "Test a tool", 3, "click"], [1, "tools-grid"], [1, "tools-list"], [1, "tool-card", 3, "expanded"], [1, "tool-card"], [1, "tool-card-header", 3, "click"], [1, "tool-title"], [1, "tool-meta"], ["title", "Parameters", 1, "param-badge"], [1, "tool-description"], [1, "tool-card-actions"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "View details", 3, "click"], [1, "tool-details"], [1, "detail-row"], [1, "detail-label"], [1, "detail-value", "mono"], [1, "detail-value"], [1, "detail-row", "full"], [1, "schema-preview"], [1, "tool-name-cell"], [1, "tool-name-info"], [1, "tool-code"], [1, "description-cell"], [1, "param-count"], [1, "actions-cell"], ["type", "button", 2, "background", "transparent", "border", "none", "cursor", "pointer", "padding", "4px", "font-size", "0.95rem", "margin-right", "4px", 3, "click", "title"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Test", 3, "click"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Details", 3, "click"], [1, "detail-row-expanded"], ["colspan", "6"], [1, "inline-details"], [1, "detail-section"], [1, "detail-section", "full"], ["Icon", "fa-solid fa-list-check", "Title", "No recent execution logs", "Message", "Logs appear when tools are executed via MCP connections"], [1, "sortable", 3, "click", "ngClass"], [1, "sort-icon", "fa-solid"], ["title", "Click for details", 1, "clickable-row", 3, "error-row"], ["title", "Click for details", 1, "clickable-row", 3, "click"], [1, "fa-solid", "fa-check-circle"], [1, "fa-solid", "fa-times-circle"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-question-circle"], [1, "tool-name"], [1, "error-message", 3, "title"], [1, "action-cell"], [1, "fa-solid", "fa-chevron-right"], [2, "position", "fixed", "inset", "0", "z-index", "10000", "background", "rgba(0,0,0,0.5)", "display", "flex", "align-items", "center", "justify-content", "center", 3, "click"], [2, "display", "flex", "flex-direction", "column", "background", "var(--mj-bg-surface,#fff)", "border-radius", "8px", "box-shadow", "0 20px 60px rgba(0,0,0,0.3)", "overflow", "hidden", "width", "600px", "max-width", "90vw", "max-height", "90vh", 3, "click"], [2, "display", "flex", "align-items", "center", "justify-content", "space-between", "padding", "16px 20px", "border-bottom", "1px solid var(--mj-border-default,#e0e0e0)", "flex-shrink", "0"], [2, "margin", "0", "font-size", "1.125rem", "font-weight", "600", "color", "var(--mj-text-primary,#333)"], [2, "display", "flex", "align-items", "center", "justify-content", "center", "width", "32px", "height", "32px", "border", "none", "background", "none", "cursor", "pointer", "color", "var(--mj-text-muted,#888)", "border-radius", "4px", 3, "click"], [1, "fa-solid", "fa-times"], [2, "flex", "1", "overflow-y", "auto", "padding", "20px", "color", "var(--mj-text-primary,#333)"], ["Variant", "error"], [2, "margin-bottom", "16px"], [2, "display", "block", "font-size", "0.875rem", "font-weight", "500", "color", "var(--mj-text-primary,#333)", "margin-bottom", "4px"], [2, "color", "var(--mj-status-error,#e53e3e)"], ["placeholder", "e.g., GitHub MCP Server", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], ["placeholder", "Optional description", "rows", "3", 1, "mj-textarea", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "change", "value"], [3, "value"], ["value", "Active"], ["value", "Inactive"], [2, "display", "flex", "align-items", "center", "gap", "8px", "padding", "16px 20px", "border-top", "1px solid var(--mj-border-default,#e0e0e0)", "flex-shrink", "0"], ["mjButton", "", "variant", "primary", 3, "click", "disabled"], ["mjButton", "", 3, "click"], ["placeholder", "https://api.example.com/mcp", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], ["placeholder", "e.g., npx", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], [2, "display", "flex", "flex-direction", "column", "background", "var(--mj-bg-surface,#fff)", "border-radius", "8px", "box-shadow", "0 20px 60px rgba(0,0,0,0.3)", "overflow", "hidden", "width", "560px", "max-width", "90vw", "max-height", "90vh", 3, "click"], ["value", ""], ["placeholder", "e.g., Zapier Connection", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], ["placeholder", "Paste bearer token here", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", "font-family", "monospace", "font-size", "0.8rem", 3, "input", "value"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)"], [2, "display", "flex", "flex-direction", "column", "background", "var(--mj-bg-surface,#fff)", "border-radius", "8px", "box-shadow", "0 20px 60px rgba(0,0,0,0.3)", "overflow", "hidden", "width", "640px", "max-width", "90vw", "max-height", "90vh", 3, "click"], [2, "font-size", "0.875rem", "font-weight", "400", "color", "var(--mj-text-muted,#888)", "margin-left", "8px"], [2, "display", "flex", "gap", "0", "padding", "10px 20px", "background", "var(--mj-bg-surface-card,#f9f9f9)", "border-bottom", "1px solid var(--mj-border-default,#e0e0e0)", "flex-shrink", "0"], [2, "font-size", "0.8rem", "font-weight", "500"], [2, "margin", "0 8px", "color", "var(--mj-text-muted,#888)"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "change"], [3, "value", "selected"], ["type", "text", "placeholder", "Search tools\u2026 (Recently used appear first)", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", "margin-bottom", "6px", 3, "input", "value"], [2, "max-height", "220px", "overflow-y", "auto", "border", "1px solid var(--mj-border-default,#e0e0e0)", "border-radius", "4px", "background", "var(--mj-bg-surface,#fff)"], [2, "padding", "8px 12px", "cursor", "pointer", "display", "flex", "align-items", "center", "gap", "8px", "border-bottom", "1px solid var(--mj-border-subtle,#eee)", 3, "background"], [2, "padding", "8px 12px", "cursor", "pointer", "display", "flex", "align-items", "center", "gap", "8px", "border-bottom", "1px solid var(--mj-border-subtle,#eee)", 3, "click"], [2, "font-size", "0.65rem", "background", "var(--mj-brand-primary,#264FAF)", "color", "#fff", "padding", "1px 6px", "border-radius", "8px", "font-weight", "600"], [2, "font-size", "0.85rem", "font-weight", "500", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], [2, "font-size", "0.7rem", "color", "var(--mj-text-muted,#888)", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], [1, "fa-solid", "fa-check", 2, "color", "var(--mj-brand-primary,#264FAF)"], [2, "text-align", "center", "padding", "24px", "color", "var(--mj-text-muted,#888)"], [1, "fa-solid", "fa-check-circle", 2, "font-size", "2rem", "margin-bottom", "8px", "display", "block"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin", "0 0 4px 0"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box"], ["rows", "3", 1, "mj-textarea", 2, "width", "100%", "box-sizing", "border-box", "font-family", "monospace", "font-size", "0.8rem", 3, "placeholder"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "placeholder"], ["value", "false"], ["value", "true"], ["rows", "3", 1, "mj-textarea", 2, "width", "100%", "box-sizing", "border-box", "font-family", "monospace", "font-size", "0.8rem", 3, "input", "placeholder"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "placeholder"], ["Variant", "success"], [2, "float", "right", "font-size", "0.75rem"], [2, "display", "block", "font-size", "0.875rem", "font-weight", "500", "margin-bottom", "4px"], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.8rem", "white-space", "pre-wrap", "word-break", "break-all", "margin", "0"], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.8rem", "white-space", "pre-wrap", "word-break", "break-all", "overflow-y", "auto", "max-height", "300px", "margin", "0"], [1, "fa-solid", "fa-arrow-right"], [1, "fa-solid", "fa-arrow-left"], ["mjButton", "", "variant", "primary", 3, "click"], [1, "fa-solid", "fa-redo"], [2, "position", "absolute", "inset", "0", "background", "rgba(0,0,0,0.35)", "pointer-events", "auto", 3, "click"], [2, "position", "absolute", "top", "0", "right", "0", "height", "100%", "width", "560px", "max-width", "95vw", "background", "var(--mj-bg-surface,#fff)", "box-shadow", "-4px 0 20px rgba(0,0,0,0.15)", "display", "flex", "flex-direction", "column", "pointer-events", "auto"], [2, "padding", "16px 20px", "border-bottom", "1px solid var(--mj-border-default,#e0e0e0)", "display", "flex", "align-items", "center", "justify-content", "space-between"], [2, "margin", "0", "font-size", "1rem", "font-weight", "600"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin-top", "4px"], [2, "font-weight", "600"], [2, "margin", "0 8px"], ["mjButton", "", "aria-label", "Close", 3, "click"], [1, "fa-solid", "fa-xmark"], [2, "flex", "1", "overflow-y", "auto", "padding", "16px 20px"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin-bottom", "4px"], [2, "font-size", "0.875rem"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin-top", "2px"], ["Size", "sm", 1, "mcp-log-section", 3, "FlushBody", "Expanded"], [2, "padding", "12px 20px", "border-top", "1px solid var(--mj-border-default,#e0e0e0)", "display", "flex", "gap", "8px", "justify-content", "flex-start"], ["mjButton", "", "variant", "primary"], ["Size", "sm", 1, "mcp-log-section", 3, "ExpandedChange", "FlushBody", "Expanded"], ["mjAccordionTitle", ""], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.75rem", "white-space", "pre-wrap", "word-break", "break-all", "overflow-y", "auto", "max-height", "240px", "margin", "0"], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.75rem", "white-space", "pre-wrap", "word-break", "break-all", "overflow-y", "auto", "max-height", "360px", "margin", "0"], [2, "background", "var(--mj-status-error-bg,#fff5f5)", "border", "1px solid var(--mj-status-error-border,#feb2b2)", "border-radius", "4px", "padding", "12px", "font-size", "0.75rem", "white-space", "pre-wrap", "word-break", "break-all", "margin", "0", "color", "var(--mj-status-error-text,#c53030)"], [2, "color", "var(--mj-status-error-text,#c53030)"]], template: function MCPDashboardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
5007
5025
|
i0.ɵɵelementStart(0, "mj-page-layout")(1, "mj-page-header", 18)(2, "div", 19);
|
|
5008
5026
|
i0.ɵɵelement(3, "mj-stat-badge", 20);
|
|
@@ -5057,7 +5075,7 @@ let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
|
|
|
5057
5075
|
i0.ɵɵconditional(ctx.ShowTestToolDialog ? 20 : -1);
|
|
5058
5076
|
i0.ɵɵadvance();
|
|
5059
5077
|
i0.ɵɵconditional(ctx.ShowLogDetailPanel && ctx.SelectedLog ? 21 : -1);
|
|
5060
|
-
} }, dependencies: [i2.NgClass, i3.NgSelectOption, i3.ɵNgSelectMultipleOption, i4.MJButtonDirective, i4.MJPageHeaderComponent, i4.MJPageLayoutComponent, i4.MJPageBodyComponent, i4.MJFilterPopoverComponent, i4.MJFilterPanelComponent, i4.MJPageSearchComponent, i4.MJTabNavComponent, i4.MJViewToggleComponent, i4.MJRefreshButtonComponent, i4.MJStatBadgeComponent, i4.MJEmptyStateComponent, i4.MJAlertComponent, i4.MJAccordionPanelComponent, i4.MJAccordionTitleDirective, i5.LoadingComponent], styles: ["\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.data-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));\n gap: 16px;\n}\n\n\n\n\n.data-grid[_ngcontent-%COMP%] > mj-empty-state[_ngcontent-%COMP%] {\n grid-column: 1 / -1;\n}\n\n.data-card[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n background: var(--mj-bg-surface);\n transition: box-shadow 0.2s ease;\n}\n\n.data-card[_ngcontent-%COMP%]:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n.card-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.card-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.card-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.card-title[_ngcontent-%COMP%] .name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 15px;\n}\n\n.card-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n}\n\n.card-actions[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n padding: 4px 8px;\n}\n\n.card-body[_ngcontent-%COMP%] {\n padding: 16px;\n}\n\n.card-body[_ngcontent-%COMP%] .description[_ngcontent-%COMP%] {\n margin: 0 0 12px 0;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.details-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 12px;\n}\n\n.detail[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.detail.full-width[_ngcontent-%COMP%] {\n grid-column: 1 / -1;\n}\n\n.detail.error[_ngcontent-%COMP%] .value[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.detail[_ngcontent-%COMP%] .label[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n font-weight: 500;\n}\n\n.detail[_ngcontent-%COMP%] .value[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.detail[_ngcontent-%COMP%] .value.url[_ngcontent-%COMP%], \n.detail[_ngcontent-%COMP%] .value.command[_ngcontent-%COMP%] {\n font-family: monospace;\n font-size: 12px;\n background: var(--mj-bg-surface-card);\n padding: 4px 8px;\n border-radius: 4px;\n word-break: break-all;\n}\n\n\n\n.status-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n}\n\n.status-active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.status-inactive[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-text-disabled) 10%, var(--mj-bg-surface));\n color: var(--mj-text-disabled);\n}\n\n.status-error[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.status-deprecated[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.status-unknown[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-text-disabled) 10%, var(--mj-bg-surface));\n color: var(--mj-text-disabled);\n}\n\n\n\n.sync-progress-banner[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-bottom: 1px solid color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n color: var(--mj-brand-primary);\n font-size: 13px;\n}\n\n.sync-progress-banner[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.sync-progress-banner[_ngcontent-%COMP%] .sync-message[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n\n\n.data-table[_ngcontent-%COMP%] {\n overflow: auto;\n background: var(--mj-bg-surface);\n border-radius: 8px;\n border: 1px solid var(--mj-border-default);\n}\n\n.data-table[_ngcontent-%COMP%] table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n}\n\n.data-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%], \n.data-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 12px 16px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.data-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n font-weight: 600;\n background: var(--mj-bg-surface-card);\n position: sticky;\n top: 0;\n}\n\n.data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:last-child td[_ngcontent-%COMP%] {\n border-bottom: none;\n}\n\n.data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr.error-row[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-error) 5%, var(--mj-bg-surface));\n}\n\n.data-table[_ngcontent-%COMP%] .tool-name[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 500;\n}\n\n.data-table[_ngcontent-%COMP%] .tool-name[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.data-table[_ngcontent-%COMP%] .error-message[_ngcontent-%COMP%] {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--mj-status-error);\n font-size: 12px;\n}\n\n.data-table[_ngcontent-%COMP%] .server-name[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n\n\n.data-table[_ngcontent-%COMP%] th.sortable[_ngcontent-%COMP%] {\n cursor: pointer;\n user-select: none;\n transition: background 0.15s ease;\n}\n\n.data-table[_ngcontent-%COMP%] th.sortable[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.data-table[_ngcontent-%COMP%] th.sortable[_ngcontent-%COMP%] .sort-icon[_ngcontent-%COMP%] {\n margin-left: 6px;\n font-size: 10px;\n color: var(--mj-border-strong);\n transition: color 0.15s ease;\n}\n\n.data-table[_ngcontent-%COMP%] th.sortable[_ngcontent-%COMP%]:hover .sort-icon[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}\n\n.data-table[_ngcontent-%COMP%] th.sorted-asc[_ngcontent-%COMP%] .sort-icon[_ngcontent-%COMP%], \n.data-table[_ngcontent-%COMP%] th.sorted-desc[_ngcontent-%COMP%] .sort-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n\n\n.status-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n\n\n\n\n\n\n\n\n\n.tools-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.server-group[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n\n.server-group-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.server-group-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.server-group.collapsed[_ngcontent-%COMP%] .server-group-header[_ngcontent-%COMP%] {\n border-bottom: none;\n}\n\n.server-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.expand-icon[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-disabled);\n transition: transform 0.2s ease;\n}\n\n.expand-icon.expanded[_ngcontent-%COMP%] {\n transform: rotate(90deg);\n}\n\n.server-name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 15px;\n color: var(--mj-text-primary);\n}\n\n.tool-count[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-secondary);\n background: rgba(0, 0, 0, 0.05);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.status-badge.small[_ngcontent-%COMP%] {\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.server-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n}\n\n\n\n\n\n.tools-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 12px;\n padding: 16px;\n}\n\n.tool-card[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n transition: all 0.2s ease;\n overflow: hidden;\n}\n\n.tool-card[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.tool-card.expanded[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n}\n\n.tool-card-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n padding: 12px 14px;\n cursor: pointer;\n}\n\n.tool-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n}\n\n.tool-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n.tool-title[_ngcontent-%COMP%] .name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.tool-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.param-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: var(--mj-bg-surface-card);\n border-radius: 10px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.param-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n.tool-description[_ngcontent-%COMP%] {\n margin: 0;\n padding: 0 14px 8px 14px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.tool-card-actions[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 8px 14px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n\n\n.tool-details[_ngcontent-%COMP%] {\n padding: 12px 14px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-row[_ngcontent-%COMP%] {\n display: flex;\n padding: 6px 0;\n font-size: 12px;\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-row.full[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 6px;\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-label[_ngcontent-%COMP%] {\n min-width: 100px;\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-value[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-value.mono[_ngcontent-%COMP%] {\n font-family: 'Consolas', 'Monaco', monospace;\n background: var(--mj-bg-surface);\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid var(--mj-border-default);\n}\n\n.schema-preview[_ngcontent-%COMP%] {\n margin: 0;\n padding: 10px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-family: 'Consolas', 'Monaco', monospace;\n font-size: 11px;\n line-height: 1.4;\n max-height: 200px;\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n\n\n\n\n.tools-list[_ngcontent-%COMP%] {\n overflow: auto;\n}\n\n.tools-list[_ngcontent-%COMP%] table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n}\n\n.tools-list[_ngcontent-%COMP%] th[_ngcontent-%COMP%], \n.tools-list[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 10px 14px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.tools-list[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n font-weight: 600;\n background: var(--mj-bg-surface-card);\n position: sticky;\n top: 0;\n}\n\n.tools-list[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n transition: background 0.15s ease;\n}\n\n.tools-list[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.tools-list[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr.expanded[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 4%, transparent);\n}\n\n.tool-name-cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.tool-name-cell[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n margin-top: 2px;\n}\n\n.tool-name-info[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.tool-name-info[_ngcontent-%COMP%] .tool-title[_ngcontent-%COMP%] {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.tool-name-info[_ngcontent-%COMP%] .tool-code[_ngcontent-%COMP%] {\n font-size: 11px;\n font-family: monospace;\n color: var(--mj-text-secondary);\n}\n\n.description-cell[_ngcontent-%COMP%] {\n max-width: 300px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.param-count[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n background: var(--mj-bg-surface-card);\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.actions-cell[_ngcontent-%COMP%] {\n white-space: nowrap;\n}\n\n\n\n.detail-row-expanded[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 0;\n background: var(--mj-bg-surface-card);\n}\n\n.inline-details[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n padding: 14px;\n border-top: 1px dashed var(--mj-border-default);\n}\n\n.inline-details[_ngcontent-%COMP%] .detail-section[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n font-size: 12px;\n}\n\n.inline-details[_ngcontent-%COMP%] .detail-section.full[_ngcontent-%COMP%] {\n flex-basis: 100%;\n flex-direction: column;\n gap: 6px;\n}\n\n.inline-details[_ngcontent-%COMP%] .detail-label[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.inline-details[_ngcontent-%COMP%] .detail-value[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n}\n\n.inline-details[_ngcontent-%COMP%] .detail-value.mono[_ngcontent-%COMP%] {\n font-family: monospace;\n}\n\n\n\n\n\n@media (max-width: 900px) {\n .mcp-dashboard[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n\n .sidebar[_ngcontent-%COMP%] {\n width: 100%;\n min-width: 100%;\n flex-direction: row;\n height: auto;\n border-right: none;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .sidebar-header[_ngcontent-%COMP%] {\n padding: 12px 16px;\n border-bottom: none;\n border-right: 1px solid var(--mj-border-default);\n }\n\n .sidebar-nav[_ngcontent-%COMP%] {\n flex-direction: row;\n padding: 8px;\n overflow-x: auto;\n flex: 1;\n }\n\n .nav-item[_ngcontent-%COMP%] {\n padding: 8px 12px;\n margin-bottom: 0;\n margin-right: 4px;\n white-space: nowrap;\n }\n\n .sidebar-stats[_ngcontent-%COMP%] {\n display: none;\n }\n\n .sidebar-footer[_ngcontent-%COMP%] {\n border-top: none;\n border-left: 1px solid var(--mj-border-default);\n padding: 8px;\n }\n\n .data-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n\n .header-actions[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n }\n\n .search-box[_ngcontent-%COMP%] {\n min-width: 100%;\n order: 1;\n }\n}\n\n@media (max-width: 600px) {\n .content-header[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n\n .header-actions[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .search-box[_ngcontent-%COMP%] {\n width: 100%;\n }\n}\n\n\n\n\n\n.clickable-row[_ngcontent-%COMP%] {\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.clickable-row[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent) !important;\n}\n\n.clickable-row[_ngcontent-%COMP%] .action-cell[_ngcontent-%COMP%] {\n color: var(--mj-border-strong);\n transition: color 0.15s ease;\n}\n\n.clickable-row[_ngcontent-%COMP%]:hover .action-cell[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.action-cell[_ngcontent-%COMP%] {\n width: 40px;\n text-align: center;\n}\n\n\n\n\n\n.card-header.clickable[_ngcontent-%COMP%] {\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.card-header.clickable[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.expand-arrow[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-disabled);\n transition: transform 0.2s ease;\n margin-right: 4px;\n}\n\n.expand-arrow.rotated[_ngcontent-%COMP%] {\n transform: rotate(90deg);\n}\n\n.data-card.expanded[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n\n\n\n\n.expanded-tools-section[_ngcontent-%COMP%] {\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.tools-section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.tools-section-header[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.no-tools-message[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.no-tools-message[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}\n\n\n\n.tools-mini-list[_ngcontent-%COMP%] {\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: 300px;\n overflow-y: auto;\n}\n\n.tool-mini-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n transition: all 0.15s ease;\n}\n\n.tool-mini-card[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 2%, transparent);\n}\n\n.tool-mini-info[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n}\n\n.tool-mini-name[_ngcontent-%COMP%] {\n font-weight: 500;\n font-size: 13px;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tool-mini-params[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.tool-mini-params[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n.tool-mini-card[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n flex-shrink: 0;\n margin-left: 8px;\n}\n\n\n\n[_nghost-%COMP%] .search-highlight, \n.search-highlight[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: inherit;\n}"] });
|
|
5078
|
+
} }, dependencies: [i3.NgClass, i4.NgSelectOption, i4.ɵNgSelectMultipleOption, i2.MJButtonDirective, i2.MJPageHeaderComponent, i2.MJPageLayoutComponent, i2.MJPageBodyComponent, i2.MJFilterPopoverComponent, i2.MJFilterPanelComponent, i2.MJPageSearchComponent, i2.MJTabNavComponent, i2.MJViewToggleComponent, i2.MJRefreshButtonComponent, i2.MJStatBadgeComponent, i2.MJEmptyStateComponent, i2.MJAlertComponent, i2.MJAccordionPanelComponent, i2.MJAccordionTitleDirective, i5.LoadingComponent], styles: ["\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.data-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));\n gap: 16px;\n}\n\n\n\n\n.data-grid[_ngcontent-%COMP%] > mj-empty-state[_ngcontent-%COMP%] {\n grid-column: 1 / -1;\n}\n\n.data-card[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n background: var(--mj-bg-surface);\n transition: box-shadow 0.2s ease;\n}\n\n.data-card[_ngcontent-%COMP%]:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n.card-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.card-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.card-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.card-title[_ngcontent-%COMP%] .name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 15px;\n}\n\n.card-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n}\n\n.card-actions[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n padding: 4px 8px;\n}\n\n.card-body[_ngcontent-%COMP%] {\n padding: 16px;\n}\n\n.card-body[_ngcontent-%COMP%] .description[_ngcontent-%COMP%] {\n margin: 0 0 12px 0;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.details-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 12px;\n}\n\n.detail[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.detail.full-width[_ngcontent-%COMP%] {\n grid-column: 1 / -1;\n}\n\n.detail.error[_ngcontent-%COMP%] .value[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.detail[_ngcontent-%COMP%] .label[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n font-weight: 500;\n}\n\n.detail[_ngcontent-%COMP%] .value[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.detail[_ngcontent-%COMP%] .value.url[_ngcontent-%COMP%], \n.detail[_ngcontent-%COMP%] .value.command[_ngcontent-%COMP%] {\n font-family: monospace;\n font-size: 12px;\n background: var(--mj-bg-surface-card);\n padding: 4px 8px;\n border-radius: 4px;\n word-break: break-all;\n}\n\n\n\n.status-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n}\n\n.status-active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.status-inactive[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-text-disabled) 10%, var(--mj-bg-surface));\n color: var(--mj-text-disabled);\n}\n\n.status-error[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.status-deprecated[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.status-unknown[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-text-disabled) 10%, var(--mj-bg-surface));\n color: var(--mj-text-disabled);\n}\n\n\n\n.sync-progress-banner[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-bottom: 1px solid color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n color: var(--mj-brand-primary);\n font-size: 13px;\n}\n\n.sync-progress-banner[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.sync-progress-banner[_ngcontent-%COMP%] .sync-message[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n\n\n.data-table[_ngcontent-%COMP%] {\n overflow: auto;\n background: var(--mj-bg-surface);\n border-radius: 8px;\n border: 1px solid var(--mj-border-default);\n}\n\n.data-table[_ngcontent-%COMP%] table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n}\n\n.data-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%], \n.data-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 12px 16px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.data-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n font-weight: 600;\n background: var(--mj-bg-surface-card);\n position: sticky;\n top: 0;\n}\n\n.data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:last-child td[_ngcontent-%COMP%] {\n border-bottom: none;\n}\n\n.data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr.error-row[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-error) 5%, var(--mj-bg-surface));\n}\n\n.data-table[_ngcontent-%COMP%] .tool-name[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 500;\n}\n\n.data-table[_ngcontent-%COMP%] .tool-name[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.data-table[_ngcontent-%COMP%] .error-message[_ngcontent-%COMP%] {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--mj-status-error);\n font-size: 12px;\n}\n\n.data-table[_ngcontent-%COMP%] .server-name[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n\n\n.data-table[_ngcontent-%COMP%] th.sortable[_ngcontent-%COMP%] {\n cursor: pointer;\n user-select: none;\n transition: background 0.15s ease;\n}\n\n.data-table[_ngcontent-%COMP%] th.sortable[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.data-table[_ngcontent-%COMP%] th.sortable[_ngcontent-%COMP%] .sort-icon[_ngcontent-%COMP%] {\n margin-left: 6px;\n font-size: 10px;\n color: var(--mj-border-strong);\n transition: color 0.15s ease;\n}\n\n.data-table[_ngcontent-%COMP%] th.sortable[_ngcontent-%COMP%]:hover .sort-icon[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}\n\n.data-table[_ngcontent-%COMP%] th.sorted-asc[_ngcontent-%COMP%] .sort-icon[_ngcontent-%COMP%], \n.data-table[_ngcontent-%COMP%] th.sorted-desc[_ngcontent-%COMP%] .sort-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n\n\n.status-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n\n\n\n\n\n\n\n\n\n.tools-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.server-group[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n\n.server-group-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.server-group-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.server-group.collapsed[_ngcontent-%COMP%] .server-group-header[_ngcontent-%COMP%] {\n border-bottom: none;\n}\n\n.server-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.expand-icon[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-disabled);\n transition: transform 0.2s ease;\n}\n\n.expand-icon.expanded[_ngcontent-%COMP%] {\n transform: rotate(90deg);\n}\n\n.server-name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 15px;\n color: var(--mj-text-primary);\n}\n\n.tool-count[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-secondary);\n background: rgba(0, 0, 0, 0.05);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.status-badge.small[_ngcontent-%COMP%] {\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.server-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n}\n\n\n\n\n\n.tools-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 12px;\n padding: 16px;\n}\n\n.tool-card[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n transition: all 0.2s ease;\n overflow: hidden;\n}\n\n.tool-card[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.tool-card.expanded[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n}\n\n.tool-card-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n padding: 12px 14px;\n cursor: pointer;\n}\n\n.tool-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n}\n\n.tool-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n.tool-title[_ngcontent-%COMP%] .name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.tool-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.param-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: var(--mj-bg-surface-card);\n border-radius: 10px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.param-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n.tool-description[_ngcontent-%COMP%] {\n margin: 0;\n padding: 0 14px 8px 14px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.tool-card-actions[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 8px 14px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n\n\n.tool-details[_ngcontent-%COMP%] {\n padding: 12px 14px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-row[_ngcontent-%COMP%] {\n display: flex;\n padding: 6px 0;\n font-size: 12px;\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-row.full[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 6px;\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-label[_ngcontent-%COMP%] {\n min-width: 100px;\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-value[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-value.mono[_ngcontent-%COMP%] {\n font-family: 'Consolas', 'Monaco', monospace;\n background: var(--mj-bg-surface);\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid var(--mj-border-default);\n}\n\n.schema-preview[_ngcontent-%COMP%] {\n margin: 0;\n padding: 10px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-family: 'Consolas', 'Monaco', monospace;\n font-size: 11px;\n line-height: 1.4;\n max-height: 200px;\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n\n\n\n\n.tools-list[_ngcontent-%COMP%] {\n overflow: auto;\n}\n\n.tools-list[_ngcontent-%COMP%] table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n}\n\n.tools-list[_ngcontent-%COMP%] th[_ngcontent-%COMP%], \n.tools-list[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 10px 14px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.tools-list[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n font-weight: 600;\n background: var(--mj-bg-surface-card);\n position: sticky;\n top: 0;\n}\n\n.tools-list[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n transition: background 0.15s ease;\n}\n\n.tools-list[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.tools-list[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr.expanded[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 4%, transparent);\n}\n\n.tool-name-cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.tool-name-cell[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n margin-top: 2px;\n}\n\n.tool-name-info[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.tool-name-info[_ngcontent-%COMP%] .tool-title[_ngcontent-%COMP%] {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.tool-name-info[_ngcontent-%COMP%] .tool-code[_ngcontent-%COMP%] {\n font-size: 11px;\n font-family: monospace;\n color: var(--mj-text-secondary);\n}\n\n.description-cell[_ngcontent-%COMP%] {\n max-width: 300px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.param-count[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n background: var(--mj-bg-surface-card);\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.actions-cell[_ngcontent-%COMP%] {\n white-space: nowrap;\n}\n\n\n\n.detail-row-expanded[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 0;\n background: var(--mj-bg-surface-card);\n}\n\n.inline-details[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n padding: 14px;\n border-top: 1px dashed var(--mj-border-default);\n}\n\n.inline-details[_ngcontent-%COMP%] .detail-section[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n font-size: 12px;\n}\n\n.inline-details[_ngcontent-%COMP%] .detail-section.full[_ngcontent-%COMP%] {\n flex-basis: 100%;\n flex-direction: column;\n gap: 6px;\n}\n\n.inline-details[_ngcontent-%COMP%] .detail-label[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.inline-details[_ngcontent-%COMP%] .detail-value[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n}\n\n.inline-details[_ngcontent-%COMP%] .detail-value.mono[_ngcontent-%COMP%] {\n font-family: monospace;\n}\n\n\n\n\n\n@media (max-width: 900px) {\n .mcp-dashboard[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n\n .sidebar[_ngcontent-%COMP%] {\n width: 100%;\n min-width: 100%;\n flex-direction: row;\n height: auto;\n border-right: none;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .sidebar-header[_ngcontent-%COMP%] {\n padding: 12px 16px;\n border-bottom: none;\n border-right: 1px solid var(--mj-border-default);\n }\n\n .sidebar-nav[_ngcontent-%COMP%] {\n flex-direction: row;\n padding: 8px;\n overflow-x: auto;\n flex: 1;\n }\n\n .nav-item[_ngcontent-%COMP%] {\n padding: 8px 12px;\n margin-bottom: 0;\n margin-right: 4px;\n white-space: nowrap;\n }\n\n .sidebar-stats[_ngcontent-%COMP%] {\n display: none;\n }\n\n .sidebar-footer[_ngcontent-%COMP%] {\n border-top: none;\n border-left: 1px solid var(--mj-border-default);\n padding: 8px;\n }\n\n .data-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n\n .header-actions[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n }\n\n .search-box[_ngcontent-%COMP%] {\n min-width: 100%;\n order: 1;\n }\n}\n\n@media (max-width: 600px) {\n .content-header[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n\n .header-actions[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .search-box[_ngcontent-%COMP%] {\n width: 100%;\n }\n}\n\n\n\n\n\n.clickable-row[_ngcontent-%COMP%] {\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.clickable-row[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent) !important;\n}\n\n.clickable-row[_ngcontent-%COMP%] .action-cell[_ngcontent-%COMP%] {\n color: var(--mj-border-strong);\n transition: color 0.15s ease;\n}\n\n.clickable-row[_ngcontent-%COMP%]:hover .action-cell[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.action-cell[_ngcontent-%COMP%] {\n width: 40px;\n text-align: center;\n}\n\n\n\n\n\n.card-header.clickable[_ngcontent-%COMP%] {\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.card-header.clickable[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.expand-arrow[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-disabled);\n transition: transform 0.2s ease;\n margin-right: 4px;\n}\n\n.expand-arrow.rotated[_ngcontent-%COMP%] {\n transform: rotate(90deg);\n}\n\n.data-card.expanded[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n\n\n\n\n.expanded-tools-section[_ngcontent-%COMP%] {\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.tools-section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.tools-section-header[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.no-tools-message[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.no-tools-message[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}\n\n\n\n.tools-mini-list[_ngcontent-%COMP%] {\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: 300px;\n overflow-y: auto;\n}\n\n.tool-mini-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n transition: all 0.15s ease;\n}\n\n.tool-mini-card[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 2%, transparent);\n}\n\n.tool-mini-info[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n}\n\n.tool-mini-name[_ngcontent-%COMP%] {\n font-weight: 500;\n font-size: 13px;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tool-mini-params[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.tool-mini-params[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n.tool-mini-card[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n flex-shrink: 0;\n margin-left: 8px;\n}\n\n\n\n[_nghost-%COMP%] .search-highlight, \n.search-highlight[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: inherit;\n}"] });
|
|
5061
5079
|
};
|
|
5062
5080
|
MCPDashboardComponent = __decorate([
|
|
5063
5081
|
RegisterClass(BaseDashboard, 'MCPDashboard')
|
|
@@ -5066,6 +5084,6 @@ export { MCPDashboardComponent };
|
|
|
5066
5084
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MCPDashboardComponent, [{
|
|
5067
5085
|
type: Component,
|
|
5068
5086
|
args: [{ standalone: false, selector: 'mj-mcp-dashboard', template: "<mj-page-layout>\n <!-- Header -->\n <mj-page-header\n Title=\"MCP Dashboard\"\n Icon=\"fa-solid fa-plug-circle-bolt\">\n <div meta>\n <mj-stat-badge [Count]=\"CurrentFilteredCount\" [Total]=\"CurrentTotalCount\" Label=\"items\"></mj-stat-badge>\n </div>\n <div actions>\n <!-- Action Buttons based on tab -->\n @switch (ActiveTab) {\n @case ('servers') {\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"createServer()\">\n <i class=\"fa-solid fa-plus\"></i>\n <span class=\"action-btn-label\">Add Server</span>\n </button>\n }\n @case ('connections') {\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"createConnection()\">\n <i class=\"fa-solid fa-plus\"></i>\n <span class=\"action-btn-label\">Add Connection</span>\n </button>\n }\n @case ('tools') {\n <mj-view-toggle\n [Options]=\"toolsViewOptions\"\n [ActiveKey]=\"ToolsViewMode\"\n (KeyChange)=\"setToolsViewMode($any($event))\">\n </mj-view-toggle>\n <button mjButton variant=\"secondary\" size=\"sm\"\n (click)=\"toggleScalableMode(!useScalablePagination)\"\n [title]=\"useScalablePagination ? 'Scale mode on: virtual scroll, paginated' : 'Enable scale mode for large tool sets'\">\n <i class=\"fa-solid\" [class.fa-bolt]=\"useScalablePagination\" [class.fa-bolt-lightning]=\"!useScalablePagination\"></i>\n <span class=\"action-btn-label\">Scale: {{ useScalablePagination ? 'ON' : 'OFF' }}</span>\n </button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"openTestToolDialog()\">\n <i class=\"fa-solid fa-play\"></i>\n <span class=\"action-btn-label\">Test Tool</span>\n </button>\n }\n @case ('logs') {\n <mj-refresh-button [Loading]=\"IsLoading\" (Clicked)=\"loadAllData()\"></mj-refresh-button>\n }\n }\n </div>\n <div toolbar>\n <!-- Tab Navigation -->\n <mj-tab-nav\n [Tabs]=\"mcpTabs\"\n [ActiveKey]=\"ActiveTab\"\n (TabChange)=\"setActiveTab($any($event))\">\n </mj-tab-nav>\n <mj-page-search\n Placeholder=\"Search\u2026\"\n [Value]=\"CurrentFilters.searchTerm\"\n (ValueChange)=\"onSearchTermChange($event)\">\n </mj-page-search>\n <mj-filter-popover\n [ActiveCount]=\"ActiveFilterCount\"\n [ShowClearAll]=\"ActiveFilterCount > 0\"\n (ClearAllRequested)=\"resetPopoverFilters()\">\n <mj-filter-panel\n [Fields]=\"mcpFilterFields\"\n [Values]=\"mcpFilterValues\"\n (ValuesChange)=\"onFilterValuesChange($event)\"\n (Reset)=\"resetPopoverFilters()\">\n </mj-filter-panel>\n </mj-filter-popover>\n </div>\n </mj-page-header>\n\n <!-- Main Content -->\n <mj-page-body [Flex]=\"true\">\n <!-- Error Message -->\n @if (ErrorMessage) {\n <mj-alert Variant=\"error\" Dismissible (Dismissed)=\"ErrorMessage = null\">{{ ErrorMessage }}</mj-alert>\n }\n\n @if (IsLoading) {\n <mj-loading text=\"Loading MCP data...\"></mj-loading>\n } @else {\n @switch (ActiveTab) {\n <!-- Servers Tab -->\n @case ('servers') {\n <div class=\"data-grid servers-grid\">\n @if (filteredServers.length === 0) {\n <mj-empty-state Icon=\"fa-solid fa-server\"\n Title=\"No MCP servers configured\"\n ActionText=\"Add Your First Server\"\n (Action)=\"createServer()\" />\n } @else {\n @for (server of filteredServers; track server.ID) {\n <div class=\"data-card\" [class.expanded]=\"isServerExpanded(server)\">\n <div class=\"card-header clickable\" (click)=\"toggleServerExpand(server)\">\n <div class=\"card-title\">\n <i class=\"fa-solid fa-chevron-right expand-arrow\" [class.rotated]=\"isServerExpanded(server)\"></i>\n <i [class]=\"getTransportIcon(server.TransportType)\"></i>\n <span class=\"name\">{{ server.Name }}</span>\n <span class=\"status-badge\" [ngClass]=\"getStatusClass(server.Status)\">\n {{ server.Status }}\n </span>\n </div>\n <div class=\"card-actions\" (click)=\"$event.stopPropagation()\">\n <button mjButton variant=\"flat\" (click)=\"editServer(server)\" title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button mjButton variant=\"flat\" (click)=\"deleteServer(server)\" title=\"Delete\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n </div>\n </div>\n <div class=\"card-body\">\n @if (server.Description) {\n <p class=\"description\">{{ server.Description }}</p>\n }\n <div class=\"details-grid\">\n <div class=\"detail\">\n <span class=\"label\">Transport</span>\n <span class=\"value\">{{ server.TransportType }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Auth</span>\n <span class=\"value\">{{ server.DefaultAuthType }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Connections</span>\n <span class=\"value\">{{ server.ConnectionCount }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Tools</span>\n <span class=\"value\">{{ server.ToolCount }}</span>\n </div>\n @if (server.ServerURL) {\n <div class=\"detail full-width\">\n <span class=\"label\">URL</span>\n <span class=\"value url\">{{ server.ServerURL }}</span>\n </div>\n }\n @if (server.Command) {\n <div class=\"detail full-width\">\n <span class=\"label\">Command</span>\n <span class=\"value command\">{{ server.Command }}</span>\n </div>\n }\n <div class=\"detail\">\n <span class=\"label\">Last Sync</span>\n <span class=\"value\">{{ formatDate(server.LastSyncAt) }}</span>\n </div>\n </div>\n </div>\n <!-- Expanded Tools Section -->\n @if (isServerExpanded(server)) {\n <div class=\"expanded-tools-section\">\n <div class=\"tools-section-header\">\n <i class=\"fa-solid fa-wrench\"></i>\n <span>Available Tools ({{ getToolsForServer(server.ID).length }})</span>\n </div>\n @if (getToolsForServer(server.ID).length === 0) {\n <div class=\"no-tools-message\">\n <i class=\"fa-solid fa-info-circle\"></i>\n No tools discovered yet. Sync a connection to discover tools.\n </div>\n } @else {\n <div class=\"tools-mini-list\">\n @for (tool of getToolsForServer(server.ID); track tool.ID) {\n <div class=\"tool-mini-card\">\n <div class=\"tool-mini-info\">\n <span class=\"tool-mini-name\">{{ tool.ToolTitle || tool.ToolName }}</span>\n <span class=\"tool-mini-params\">\n <i class=\"fa-solid fa-sliders\"></i>\n {{ getParamCount(tool) }} params\n </span>\n </div>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"runToolFromCard(tool); $event.stopPropagation()\"\n title=\"Test this tool\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n }\n\n <!-- Connections Tab -->\n @case ('connections') {\n <div class=\"data-grid connections-grid\">\n @if (filteredConnections.length === 0) {\n <mj-empty-state Icon=\"fa-solid fa-link\"\n Title=\"No connections configured\"\n ActionText=\"Add Your First Connection\"\n (Action)=\"createConnection()\" />\n } @else {\n @for (conn of filteredConnections; track conn.ID) {\n <div class=\"data-card\" [class.expanded]=\"isConnectionExpanded(conn)\">\n <div class=\"card-header clickable\" (click)=\"toggleConnectionExpand(conn)\">\n <div class=\"card-title\">\n <i class=\"fa-solid fa-chevron-right expand-arrow\" [class.rotated]=\"isConnectionExpanded(conn)\"></i>\n <i class=\"fa-solid fa-link\"></i>\n <span class=\"name\">{{ conn.Name }}</span>\n <span class=\"status-badge\" [ngClass]=\"getStatusClass(conn.Status)\">\n {{ conn.Status }}\n </span>\n </div>\n <div class=\"card-actions\" (click)=\"$event.stopPropagation()\">\n <button mjButton\n variant=\"flat\"\n (click)=\"syncConnectionTools(conn)\"\n [disabled]=\"isSyncing(conn.ID)\"\n title=\"Sync Tools\">\n @if (isSyncing(conn.ID)) {\n <i class=\"fa-solid fa-sync fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-sync\"></i>\n }\n </button>\n <button mjButton variant=\"flat\" (click)=\"editConnection(conn)\" title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button mjButton variant=\"flat\" (click)=\"deleteConnection(conn)\" title=\"Delete\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n </div>\n </div>\n <!-- Sync Progress Banner -->\n @if (isSyncing(conn.ID)) {\n <div class=\"sync-progress-banner\">\n <i class=\"fa-solid fa-circle-notch fa-spin\"></i>\n <span class=\"sync-message\">{{ getSyncProgressMessage(conn.ID) || 'Syncing...' }}</span>\n </div>\n }\n <!-- Sync Result Banner -->\n @if (getSyncState(conn.ID)?.lastResult && !isSyncing(conn.ID)) {\n <mj-alert Size=\"sm\"\n [Variant]=\"getSyncState(conn.ID)?.lastResult?.Success ? 'success' : 'error'\">\n @if (getSyncState(conn.ID)?.lastResult?.Success) {\n <span>Synced: {{ getSyncState(conn.ID)?.lastResult?.Added }} added,\n {{ getSyncState(conn.ID)?.lastResult?.Updated }} updated,\n {{ getSyncState(conn.ID)?.lastResult?.Deprecated }} deprecated\n </span>\n } @else {\n <span>{{ getSyncState(conn.ID)?.lastResult?.ErrorMessage }}</span>\n }\n </mj-alert>\n }\n <div class=\"card-body\">\n @if (conn.Description) {\n <p class=\"description\">{{ conn.Description }}</p>\n }\n <div class=\"details-grid\">\n <div class=\"detail\">\n <span class=\"label\">Server</span>\n <span class=\"value\">{{ conn.ServerName }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Auto Sync</span>\n <span class=\"value\">{{ conn.AutoSyncTools ? 'Yes' : 'No' }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Logging</span>\n <span class=\"value\">{{ conn.LogToolCalls ? 'Enabled' : 'Disabled' }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Last Connected</span>\n <span class=\"value\">{{ formatDate(conn.LastConnectedAt) }}</span>\n </div>\n @if (conn.LastErrorMessage) {\n <div class=\"detail full-width error\">\n <span class=\"label\">Last Error</span>\n <span class=\"value\">{{ conn.LastErrorMessage }}</span>\n </div>\n }\n </div>\n </div>\n <!-- Expanded Tools Section -->\n @if (isConnectionExpanded(conn)) {\n <div class=\"expanded-tools-section\">\n <div class=\"tools-section-header\">\n <i class=\"fa-solid fa-wrench\"></i>\n <span>Available Tools ({{ getToolsForConnection(conn.ID).length }})</span>\n </div>\n @if (getToolsForConnection(conn.ID).length === 0) {\n <div class=\"no-tools-message\">\n <i class=\"fa-solid fa-info-circle\"></i>\n No tools discovered yet. Click the sync button to discover tools.\n </div>\n } @else {\n <div class=\"tools-mini-list\">\n @for (tool of getToolsForConnection(conn.ID); track tool.ID) {\n <div class=\"tool-mini-card\">\n <div class=\"tool-mini-info\">\n <span class=\"tool-mini-name\">{{ tool.ToolTitle || tool.ToolName }}</span>\n <span class=\"tool-mini-params\">\n <i class=\"fa-solid fa-sliders\"></i>\n {{ getParamCount(tool) }} params\n </span>\n </div>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"runToolFromCard(tool, conn); $event.stopPropagation()\"\n title=\"Test this tool with this connection\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n }\n\n <!-- Tools Tab -->\n @case ('tools') {\n @if (useScalablePagination) {\n <div style=\"padding:8px 12px;font-size:0.8rem;color:var(--mj-text-secondary,#475569);background:var(--mj-bg-surface-card,#f8f9fa);border-radius:4px;margin-bottom:6px\">\n Showing <strong>{{ visiblePagedTools.length }}</strong> of <strong>{{ scaleDenominator() }}</strong> tools\n </div>\n <div style=\"height:calc(100vh - 260px);width:100%;overflow-y:auto;border:1px solid var(--mj-border-subtle,#eee);border-radius:4px\" (scroll)=\"onToolsScrollNative($event)\">\n @for (tool of visiblePagedTools; track tool.ID) {\n <div style=\"display:flex;align-items:center;gap:12px;padding:12px 14px;border-bottom:1px solid var(--mj-border-subtle,#eee);height:72px;box-sizing:border-box\">\n <i class=\"fa-solid fa-wrench\" style=\"color:var(--mj-brand-primary,#264FAF)\"></i>\n <div style=\"flex:1;min-width:0\">\n <div style=\"font-weight:500;font-size:0.875rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ tool.ToolTitle || tool.ToolName }}</div>\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ tool.ServerName }} \u00B7 {{ tool.ToolName }}</div>\n </div>\n <button type=\"button\" (click)=\"toggleFavorite(tool.ID, $event)\" [title]=\"isFavorited(tool.ID) ? 'Unfavorite' : 'Favorite'\" style=\"background:transparent;border:none;cursor:pointer;padding:4px;font-size:1rem\" [style.color]=\"isFavorited(tool.ID) ? 'var(--mj-status-warning, #f59e0b)' : 'var(--mj-text-muted, #bbb)'\">\n <i [class]=\"isFavorited(tool.ID) ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(tool.Status)\">{{ tool.Status }}</span>\n </div>\n }\n @if (toolsLoading) {\n <div style=\"padding:12px;text-align:center;font-size:0.8rem;color:var(--mj-text-muted,#888)\">Loading more\u2026</div>\n }\n </div>\n } @else if (ServerGroups.length === 0) {\n <mj-empty-state Icon=\"fa-solid fa-wrench\"\n Title=\"No tools discovered yet\"\n Message=\"Tools are discovered when connections sync with MCP servers\" />\n } @else {\n <div class=\"tools-container\">\n <!-- Server Groups -->\n @for (group of ServerGroups; track group.server.ID) {\n <div class=\"server-group\" [class.collapsed]=\"!group.expanded\">\n <!-- Server Group Header -->\n <div class=\"server-group-header\" (click)=\"toggleServerGroup(group)\">\n <div class=\"server-info\">\n <i class=\"fa-solid fa-chevron-right expand-icon\" [class.expanded]=\"group.expanded\"></i>\n <i [class]=\"getTransportIcon(group.server.TransportType)\"></i>\n <span class=\"server-name\">{{ group.server.Name }}</span>\n <span class=\"tool-count\">{{ getServerToolCount(group.server.ID) }} tools</span>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(group.server.Status)\">\n {{ group.server.Status }}\n </span>\n </div>\n <div class=\"server-actions\" (click)=\"$event.stopPropagation()\">\n <button mjButton variant=\"flat\" (click)=\"openTestToolDialog(undefined, undefined)\" title=\"Test a tool\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n </div>\n </div>\n\n <!-- Tools Content -->\n @if (group.expanded) {\n <!-- Card View -->\n @if (ToolsViewMode === 'card') {\n <div class=\"tools-grid\">\n @for (tool of group.tools; track tool.ID) {\n <div class=\"tool-card\" [class.expanded]=\"isToolExpanded(tool)\">\n <div class=\"tool-card-header\" (click)=\"toggleToolExpand(tool)\">\n <div class=\"tool-title\">\n <i class=\"fa-solid fa-wrench\"></i>\n <span class=\"name\">{{ tool.ToolTitle || tool.ToolName }}</span>\n </div>\n <div class=\"tool-meta\">\n <span class=\"param-badge\" title=\"Parameters\">\n <i class=\"fa-solid fa-sliders\"></i>\n {{ getParamCount(tool) }}\n </span>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(tool.Status)\">\n {{ tool.Status }}\n </span>\n </div>\n </div>\n @if (tool.ToolDescription) {\n <p class=\"tool-description\">{{ tool.ToolDescription }}</p>\n }\n <div class=\"tool-card-actions\">\n <button type=\"button\" (click)=\"toggleFavorite(tool.ID, $event)\" [title]=\"isFavorited(tool.ID) ? 'Unfavorite' : 'Favorite'\" style=\"background:transparent;border:none;cursor:pointer;padding:4px;font-size:1rem\" [style.color]=\"isFavorited(tool.ID) ? 'var(--mj-status-warning, #f59e0b)' : 'var(--mj-text-muted, #bbb)'\">\n <i [class]=\"isFavorited(tool.ID) ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"openTestToolDialog(tool)\" title=\"Test this tool\">\n <i class=\"fa-solid fa-play\"></i>\n Test\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"toggleToolExpand(tool)\" title=\"View details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!isToolExpanded(tool)\" [class.fa-chevron-up]=\"isToolExpanded(tool)\"></i>\n {{ isToolExpanded(tool) ? 'Less' : 'More' }}\n </button>\n </div>\n <!-- Expanded Details -->\n @if (isToolExpanded(tool)) {\n <div class=\"tool-details\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Tool Name:</span>\n <span class=\"detail-value mono\">{{ tool.ToolName }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Parameters:</span>\n <span class=\"detail-value\">\n {{ getParamCount(tool) }} total, {{ getRequiredParamCount(tool) }} required\n </span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Discovered:</span>\n <span class=\"detail-value\">{{ formatDate(tool.DiscoveredAt) }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Last Seen:</span>\n <span class=\"detail-value\">{{ formatDate(tool.LastSeenAt) }}</span>\n </div>\n @if (tool.InputSchema) {\n <div class=\"detail-row full\">\n <span class=\"detail-label\">Input Schema:</span>\n <pre class=\"schema-preview\">{{ getFormattedInputSchema(tool) }}</pre>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n } @else {\n <!-- List View -->\n <div class=\"tools-list\">\n <table>\n <thead>\n <tr>\n <th>Tool</th>\n <th>Description</th>\n <th>Params</th>\n <th>Status</th>\n <th>Last Seen</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (tool of group.tools; track tool.ID) {\n <tr [class.expanded]=\"isToolExpanded(tool)\">\n <td class=\"tool-name-cell\">\n <i class=\"fa-solid fa-wrench\"></i>\n <div class=\"tool-name-info\">\n <span class=\"tool-title\">{{ tool.ToolTitle || tool.ToolName }}</span>\n @if (tool.ToolTitle) {\n <span class=\"tool-code\">{{ tool.ToolName }}</span>\n }\n </div>\n </td>\n <td class=\"description-cell\">{{ tool.ToolDescription || '-' }}</td>\n <td>\n <span class=\"param-count\">{{ getParamCount(tool) }}</span>\n </td>\n <td>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(tool.Status)\">\n {{ tool.Status }}\n </span>\n </td>\n <td>{{ formatDate(tool.LastSeenAt) }}</td>\n <td class=\"actions-cell\">\n <button type=\"button\" (click)=\"toggleFavorite(tool.ID, $event)\" [title]=\"isFavorited(tool.ID) ? 'Unfavorite' : 'Favorite'\" style=\"background:transparent;border:none;cursor:pointer;padding:4px;font-size:0.95rem;margin-right:4px\" [style.color]=\"isFavorited(tool.ID) ? 'var(--mj-status-warning, #f59e0b)' : 'var(--mj-text-muted, #bbb)'\">\n <i [class]=\"isFavorited(tool.ID) ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"openTestToolDialog(tool)\" title=\"Test\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"toggleToolExpand(tool)\" title=\"Details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!isToolExpanded(tool)\" [class.fa-chevron-up]=\"isToolExpanded(tool)\"></i>\n </button>\n </td>\n </tr>\n @if (isToolExpanded(tool)) {\n <tr class=\"detail-row-expanded\">\n <td colspan=\"6\">\n <div class=\"inline-details\">\n <div class=\"detail-section\">\n <span class=\"detail-label\">Tool Name:</span>\n <span class=\"detail-value mono\">{{ tool.ToolName }}</span>\n </div>\n <div class=\"detail-section\">\n <span class=\"detail-label\">Discovered:</span>\n <span class=\"detail-value\">{{ formatDate(tool.DiscoveredAt) }}</span>\n </div>\n <div class=\"detail-section\">\n <span class=\"detail-label\">Required Params:</span>\n <span class=\"detail-value\">{{ getRequiredParamCount(tool) }}</span>\n </div>\n @if (tool.InputSchema) {\n <div class=\"detail-section full\">\n <span class=\"detail-label\">Input Schema:</span>\n <pre class=\"schema-preview\">{{ getFormattedInputSchema(tool) }}</pre>\n </div>\n }\n </div>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n }\n }\n </div>\n }\n </div>\n }\n }\n\n <!-- Logs Tab -->\n @case ('logs') {\n <div class=\"data-table\">\n @if (filteredLogs.length === 0) {\n <mj-empty-state Icon=\"fa-solid fa-list-check\"\n Title=\"No recent execution logs\"\n Message=\"Logs appear when tools are executed via MCP connections\" />\n } @else {\n <table>\n <thead>\n <tr>\n <th class=\"sortable\" [ngClass]=\"getLogSortClass('status')\" (click)=\"onLogSortColumn('status')\">\n Status\n <i class=\"sort-icon fa-solid\" [class.fa-sort-up]=\"LogsSortColumn === 'status' && LogsSortAscending\" [class.fa-sort-down]=\"LogsSortColumn === 'status' && !LogsSortAscending\" [class.fa-sort]=\"LogsSortColumn !== 'status'\"></i>\n </th>\n <th class=\"sortable\" [ngClass]=\"getLogSortClass('server')\" (click)=\"onLogSortColumn('server')\">\n Server\n <i class=\"sort-icon fa-solid\" [class.fa-sort-up]=\"LogsSortColumn === 'server' && LogsSortAscending\" [class.fa-sort-down]=\"LogsSortColumn === 'server' && !LogsSortAscending\" [class.fa-sort]=\"LogsSortColumn !== 'server'\"></i>\n </th>\n <th class=\"sortable\" [ngClass]=\"getLogSortClass('tool')\" (click)=\"onLogSortColumn('tool')\">\n Tool\n <i class=\"sort-icon fa-solid\" [class.fa-sort-up]=\"LogsSortColumn === 'tool' && LogsSortAscending\" [class.fa-sort-down]=\"LogsSortColumn === 'tool' && !LogsSortAscending\" [class.fa-sort]=\"LogsSortColumn !== 'tool'\"></i>\n </th>\n <th class=\"sortable\" [ngClass]=\"getLogSortClass('connection')\" (click)=\"onLogSortColumn('connection')\">\n Connection\n <i class=\"sort-icon fa-solid\" [class.fa-sort-up]=\"LogsSortColumn === 'connection' && LogsSortAscending\" [class.fa-sort-down]=\"LogsSortColumn === 'connection' && !LogsSortAscending\" [class.fa-sort]=\"LogsSortColumn !== 'connection'\"></i>\n </th>\n <th class=\"sortable\" [ngClass]=\"getLogSortClass('started')\" (click)=\"onLogSortColumn('started')\">\n Started\n <i class=\"sort-icon fa-solid\" [class.fa-sort-up]=\"LogsSortColumn === 'started' && LogsSortAscending\" [class.fa-sort-down]=\"LogsSortColumn === 'started' && !LogsSortAscending\" [class.fa-sort]=\"LogsSortColumn !== 'started'\"></i>\n </th>\n <th class=\"sortable\" [ngClass]=\"getLogSortClass('duration')\" (click)=\"onLogSortColumn('duration')\">\n Duration\n <i class=\"sort-icon fa-solid\" [class.fa-sort-up]=\"LogsSortColumn === 'duration' && LogsSortAscending\" [class.fa-sort-down]=\"LogsSortColumn === 'duration' && !LogsSortAscending\" [class.fa-sort]=\"LogsSortColumn !== 'duration'\"></i>\n </th>\n <th class=\"sortable\" [ngClass]=\"getLogSortClass('error')\" (click)=\"onLogSortColumn('error')\">\n Error\n <i class=\"sort-icon fa-solid\" [class.fa-sort-up]=\"LogsSortColumn === 'error' && LogsSortAscending\" [class.fa-sort-down]=\"LogsSortColumn === 'error' && !LogsSortAscending\" [class.fa-sort]=\"LogsSortColumn !== 'error'\"></i>\n </th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (log of filteredLogs; track log.ID) {\n <tr [class.error-row]=\"log.Status === 'Error'\"\n class=\"clickable-row\"\n (click)=\"onLogClick(log)\"\n title=\"Click for details\">\n <td>\n <span class=\"status-badge\" [ngClass]=\"getStatusClass(log.Status)\">\n @switch (log.Status) {\n @case ('Success') {\n <i class=\"fa-solid fa-check-circle\"></i>\n }\n @case ('Error') {\n <i class=\"fa-solid fa-times-circle\"></i>\n }\n @case ('Running') {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n @default {\n <i class=\"fa-solid fa-question-circle\"></i>\n }\n }\n {{ log.Status || 'Unknown' }}\n </span>\n </td>\n <td class=\"server-name\">{{ log.ServerName }}</td>\n <td class=\"tool-name\">{{ log.ToolName }}</td>\n <td>{{ log.ConnectionName }}</td>\n <td>{{ formatDate(log.StartedAt) }}</td>\n <td>{{ formatDuration(log.DurationMs) }}</td>\n <td class=\"error-message\" [title]=\"log.ErrorMessage || ''\">\n {{ log.ErrorMessage || '-' }}\n </td>\n <td class=\"action-cell\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </td>\n </tr>\n }\n </tbody>\n </table>\n }\n </div>\n }\n }\n }\n </mj-page-body>\n</mj-page-layout>\n\n<!-- Server Dialog (inline \u2014 avoids sub-component DI issues) -->\n@if (ShowServerDialog) {\n <div style=\"position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\" (click)=\"cancelServerForm()\">\n <div style=\"display:flex;flex-direction:column;background:var(--mj-bg-surface,#fff);border-radius:8px;box-shadow:0 20px 60px rgba(0,0,0,0.3);overflow:hidden;width:600px;max-width:90vw;max-height:90vh\" (click)=\"$event.stopPropagation()\">\n <!-- Title bar -->\n <div style=\"display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <h2 style=\"margin:0;font-size:1.125rem;font-weight:600;color:var(--mj-text-primary,#333)\">{{ EditingServer ? 'Edit MCP Server' : 'Add MCP Server' }}</h2>\n <button style=\"display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:none;cursor:pointer;color:var(--mj-text-muted,#888);border-radius:4px\" (click)=\"cancelServerForm()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Body -->\n <div style=\"flex:1;overflow-y:auto;padding:20px;color:var(--mj-text-primary,#333)\">\n @if (ServerFormError) {\n <mj-alert Variant=\"error\">{{ ServerFormError }}</mj-alert>\n }\n <!-- Name -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Name <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #sfName class=\"mj-input\" placeholder=\"e.g., GitHub MCP Server\" [value]=\"ServerForm.Name\" (input)=\"ServerForm.Name = sfName.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n <!-- Description -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Description</label>\n <textarea #sfDesc class=\"mj-textarea\" placeholder=\"Optional description\" [value]=\"ServerForm.Description\" (input)=\"ServerForm.Description = sfDesc.value\" rows=\"3\" style=\"width:100%;box-sizing:border-box\"></textarea>\n </div>\n <!-- Transport Type -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Transport Type <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #sfTransport class=\"mj-input\" [value]=\"ServerForm.TransportType\" (change)=\"ServerForm.TransportType = sfTransport.value\" style=\"width:100%;box-sizing:border-box\">\n @for (t of TransportTypes; track t.value) {\n <option [value]=\"t.value\">{{ t.label }}</option>\n }\n </select>\n </div>\n <!-- Server URL (for HTTP-based transports) -->\n @if (ServerForm.TransportType === 'StreamableHTTP' || ServerForm.TransportType === 'SSE' || ServerForm.TransportType === 'WebSocket') {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Server URL <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #sfUrl class=\"mj-input\" placeholder=\"https://api.example.com/mcp\" [value]=\"ServerForm.ServerURL\" (input)=\"ServerForm.ServerURL = sfUrl.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n }\n <!-- Command (for Stdio) -->\n @if (ServerForm.TransportType === 'Stdio') {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Command <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #sfCmd class=\"mj-input\" placeholder=\"e.g., npx\" [value]=\"ServerForm.Command\" (input)=\"ServerForm.Command = sfCmd.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n }\n <!-- Auth Type -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Default Auth Type</label>\n <select #sfAuth class=\"mj-input\" [value]=\"ServerForm.DefaultAuthType\" (change)=\"ServerForm.DefaultAuthType = sfAuth.value\" style=\"width:100%;box-sizing:border-box\">\n @for (a of AuthTypes; track a.value) {\n <option [value]=\"a.value\">{{ a.label }}</option>\n }\n </select>\n </div>\n <!-- Status -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Status</label>\n <select #sfStatus class=\"mj-input\" [value]=\"ServerForm.Status\" (change)=\"ServerForm.Status = sfStatus.value\" style=\"width:100%;box-sizing:border-box\">\n <option value=\"Active\">Active</option>\n <option value=\"Inactive\">Inactive</option>\n </select>\n </div>\n </div>\n <!-- Actions -->\n <div style=\"display:flex;align-items:center;gap:8px;padding:16px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <button mjButton variant=\"primary\" (click)=\"saveServerForm()\" [disabled]=\"ServerFormSaving\">\n @if (ServerFormSaving) { <i class=\"fa-solid fa-spinner fa-spin\"></i> }\n {{ EditingServer ? 'Update' : 'Create' }}\n </button>\n <button mjButton (click)=\"cancelServerForm()\">Cancel</button>\n </div>\n </div>\n </div>\n}\n\n<!-- Connection Dialog (inline) -->\n@if (ShowConnectionDialog) {\n <div style=\"position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\" (click)=\"cancelConnectionForm()\">\n <div style=\"display:flex;flex-direction:column;background:var(--mj-bg-surface,#fff);border-radius:8px;box-shadow:0 20px 60px rgba(0,0,0,0.3);overflow:hidden;width:560px;max-width:90vw;max-height:90vh\" (click)=\"$event.stopPropagation()\">\n <div style=\"display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <h2 style=\"margin:0;font-size:1.125rem;font-weight:600;color:var(--mj-text-primary,#333)\">Add Connection</h2>\n <button style=\"display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:none;cursor:pointer;color:var(--mj-text-muted,#888);border-radius:4px\" (click)=\"cancelConnectionForm()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div style=\"flex:1;overflow-y:auto;padding:20px;color:var(--mj-text-primary,#333)\">\n @if (ConnectionFormError) {\n <mj-alert Variant=\"error\">{{ ConnectionFormError }}</mj-alert>\n }\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Server <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #cfServer class=\"mj-input\" [value]=\"ConnectionForm.MCPServerID\" (change)=\"ConnectionForm.MCPServerID = cfServer.value\" style=\"width:100%;box-sizing:border-box\">\n <option value=\"\">\u2014 select server \u2014</option>\n @for (s of servers; track s.ID) {\n <option [value]=\"s.ID\">{{ s.Name }}</option>\n }\n </select>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Name <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #cfName class=\"mj-input\" placeholder=\"e.g., Zapier Connection\" [value]=\"ConnectionForm.Name\" (input)=\"ConnectionForm.Name = cfName.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Bearer Token</label>\n <input #cfToken class=\"mj-input\" placeholder=\"Paste bearer token here\" [value]=\"ConnectionForm.BearerToken\" (input)=\"ConnectionForm.BearerToken = cfToken.value\" style=\"width:100%;box-sizing:border-box;font-family:monospace;font-size:0.8rem\" />\n <span style=\"font-size:0.75rem;color:var(--mj-text-muted,#888)\">Will be stored as a Credential and linked to this connection</span>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Status</label>\n <select #cfStatus class=\"mj-input\" [value]=\"ConnectionForm.Status\" (change)=\"ConnectionForm.Status = cfStatus.value\" style=\"width:100%;box-sizing:border-box\">\n <option value=\"Active\">Active</option>\n <option value=\"Inactive\">Inactive</option>\n </select>\n </div>\n </div>\n <div style=\"display:flex;align-items:center;gap:8px;padding:16px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <button mjButton variant=\"primary\" (click)=\"saveConnectionForm()\" [disabled]=\"ConnectionFormSaving\">\n @if (ConnectionFormSaving) { <i class=\"fa-solid fa-spinner fa-spin\"></i> }\n Create\n </button>\n <button mjButton (click)=\"cancelConnectionForm()\">Cancel</button>\n </div>\n </div>\n </div>\n}\n\n<!-- Test Tool Dialog (inline) -->\n@if (ShowTestToolDialog) {\n <div style=\"position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\" (click)=\"testCloseDialog()\">\n <div style=\"display:flex;flex-direction:column;background:var(--mj-bg-surface,#fff);border-radius:8px;box-shadow:0 20px 60px rgba(0,0,0,0.3);overflow:hidden;width:640px;max-width:90vw;max-height:90vh\" (click)=\"$event.stopPropagation()\">\n <!-- Title bar -->\n <div style=\"display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <h2 style=\"margin:0;font-size:1.125rem;font-weight:600;color:var(--mj-text-primary,#333)\">\n Test MCP Tool\n @if (TestStep !== 'select') {\n <span style=\"font-size:0.875rem;font-weight:400;color:var(--mj-text-muted,#888);margin-left:8px\">\u2014 {{ TestSelectedTool?.ToolTitle || TestSelectedTool?.ToolName }}</span>\n }\n </h2>\n <button style=\"display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:none;cursor:pointer;color:var(--mj-text-muted,#888);border-radius:4px\" (click)=\"testCloseDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Step indicator -->\n <div style=\"display:flex;gap:0;padding:10px 20px;background:var(--mj-bg-surface-card,#f9f9f9);border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <span style=\"font-size:0.8rem;font-weight:500;\" [style.color]=\"TestStep==='select' ? 'var(--mj-brand-primary,#264FAF)' : 'var(--mj-text-muted,#888)'\">1. Select</span>\n <span style=\"margin:0 8px;color:var(--mj-text-muted,#888)\">\u203A</span>\n <span style=\"font-size:0.8rem;font-weight:500;\" [style.color]=\"TestStep==='configure' ? 'var(--mj-brand-primary,#264FAF)' : 'var(--mj-text-muted,#888)'\">2. Configure</span>\n <span style=\"margin:0 8px;color:var(--mj-text-muted,#888)\">\u203A</span>\n <span style=\"font-size:0.8rem;font-weight:500;\" [style.color]=\"TestStep==='results' ? 'var(--mj-brand-primary,#264FAF)' : 'var(--mj-text-muted,#888)'\">3. Results</span>\n </div>\n <!-- Body -->\n <div style=\"flex:1;overflow-y:auto;padding:20px;color:var(--mj-text-primary,#333)\">\n @if (TestStep === 'select') {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Server <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #tSrv class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (change)=\"onTestServerChange(tSrv.value)\">\n <option value=\"\">\u2014 select server \u2014</option>\n @for (s of servers; track s.ID) {\n <option [value]=\"s.ID\" [selected]=\"TestToolServerID === s.ID\">{{ s.Name }}</option>\n }\n </select>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Connection <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #tConn class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" [attr.disabled]=\"!TestToolServerID ? true : null\" (change)=\"onTestConnectionChange(tConn.value)\">\n <option value=\"\">\u2014 select connection \u2014</option>\n @for (c of TestFilteredConnections; track c.ID) {\n <option [value]=\"c.ID\" [selected]=\"TestToolConnectionID === c.ID\">{{ c.Name }}</option>\n }\n </select>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Tool <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #tSearch type=\"text\" class=\"mj-input\" style=\"width:100%;box-sizing:border-box;margin-bottom:6px\" placeholder=\"Search tools\u2026 (Recently used appear first)\" [value]=\"TestToolSearch\" (input)=\"onTestToolSearchChange(tSearch.value)\" [attr.disabled]=\"!TestToolServerID ? true : null\" />\n <div style=\"max-height:220px;overflow-y:auto;border:1px solid var(--mj-border-default,#e0e0e0);border-radius:4px;background:var(--mj-bg-surface,#fff)\">\n @if (TestComboboxTools.length === 0) {\n <div style=\"padding:12px;text-align:center;font-size:0.8rem;color:var(--mj-text-muted,#888)\">No tools match.</div>\n }\n @for (t of TestComboboxTools; track t.ID) {\n <div (click)=\"pickTestTool(t.ID)\" style=\"padding:8px 12px;cursor:pointer;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--mj-border-subtle,#eee)\" [style.background]=\"TestToolID === t.ID ? 'color-mix(in srgb, var(--mj-brand-primary,#264FAF) 12%, var(--mj-bg-surface,#fff))' : null\">\n @if (isRecentTestTool(t.ID)) {\n <span style=\"font-size:0.65rem;background:var(--mj-brand-primary,#264FAF);color:#fff;padding:1px 6px;border-radius:8px;font-weight:600\">Recent</span>\n }\n <div style=\"flex:1;min-width:0\">\n <div style=\"font-size:0.85rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ t.ToolTitle || t.ToolName }}</div>\n @if (t.ToolTitle && t.ToolName !== t.ToolTitle) {\n <div style=\"font-size:0.7rem;color:var(--mj-text-muted,#888);overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ t.ToolName }}</div>\n }\n </div>\n @if (TestToolID === t.ID) {\n <i class=\"fa-solid fa-check\" style=\"color:var(--mj-brand-primary,#264FAF)\"></i>\n }\n </div>\n }\n </div>\n </div>\n } @else if (TestStep === 'configure') {\n @if (TestParamConfigs.length === 0) {\n <div style=\"text-align:center;padding:24px;color:var(--mj-text-muted,#888)\">\n <i class=\"fa-solid fa-check-circle\" style=\"font-size:2rem;margin-bottom:8px;display:block\"></i>\n <p>This tool requires no parameters.</p>\n </div>\n } @else {\n @for (p of TestParamConfigs; track p.name) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">\n {{ p.name }} @if (p.required) { <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span> }\n </label>\n @if (p.description) {\n <p style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin:0 0 4px 0\">{{ p.description }}</p>\n }\n @if (p.enumValues.length > 0) {\n <select #enumEl class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (change)=\"testSetParam(p.name, enumEl.value)\">\n <option value=\"\">\u2014 select \u2014</option>\n @for (v of p.enumValues; track $index) { <option [value]=\"$any(v)\">{{ $any(v) }}</option> }\n </select>\n } @else if (p.type === 'boolean') {\n <select #boolEl class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (change)=\"testSetParam(p.name, boolEl.value)\">\n <option value=\"false\">false</option>\n <option value=\"true\">true</option>\n </select>\n } @else if (p.type === 'array' || p.type === 'object') {\n <textarea #jsonEl class=\"mj-textarea\" rows=\"3\" style=\"width:100%;box-sizing:border-box;font-family:monospace;font-size:0.8rem\" (input)=\"testSetParam(p.name, jsonEl.value)\" [placeholder]=\"'Enter JSON ' + p.type + '...'\"></textarea>\n } @else {\n <input #strEl class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (input)=\"testSetParam(p.name, strEl.value)\" [placeholder]=\"p.description || 'Enter value...'\" />\n }\n </div>\n }\n }\n } @else {\n @if (TestExecutionResult?.Success) {\n <mj-alert Variant=\"success\">Execution Successful @if (TestExecutionResult?.DurationMs) { <span style=\"float:right;font-size:0.75rem\">{{ TestExecutionResult?.DurationMs }}ms</span> }</mj-alert>\n } @else {\n <mj-alert Variant=\"error\">Execution Failed</mj-alert>\n }\n @if (TestExecutionResult?.ErrorMessage) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:4px\">Error</label>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.8rem;white-space:pre-wrap;word-break:break-all;margin:0\">{{ TestExecutionResult?.ErrorMessage }}</pre>\n </div>\n }\n @if (TestExecutionResult?.Result) {\n <div>\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:4px\">Result</label>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.8rem;white-space:pre-wrap;word-break:break-all;overflow-y:auto;max-height:300px;margin:0\">{{ formatTestResult(TestExecutionResult?.Result) }}</pre>\n </div>\n }\n }\n </div>\n <!-- Actions -->\n <div style=\"display:flex;align-items:center;gap:8px;padding:16px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n @if (TestStep === 'select') {\n <button mjButton variant=\"primary\" (click)=\"testProceedToConfig()\" [disabled]=\"!TestCanProceed\">Next <i class=\"fa-solid fa-arrow-right\"></i></button>\n <button mjButton (click)=\"testCloseDialog()\">Cancel</button>\n } @else if (TestStep === 'configure') {\n <button mjButton variant=\"primary\" (click)=\"testExecuteTool()\" [disabled]=\"!TestIsValid || TestIsExecuting\">\n @if (TestIsExecuting) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Executing... } @else { <i class=\"fa-solid fa-play\"></i> Execute }\n </button>\n <button mjButton (click)=\"testGoBack()\"><i class=\"fa-solid fa-arrow-left\"></i> Back</button>\n } @else {\n <button mjButton variant=\"primary\" (click)=\"testRunAgain()\"><i class=\"fa-solid fa-redo\"></i> Run Again</button>\n <button mjButton (click)=\"testGoBack()\">Edit Params</button>\n <button mjButton (click)=\"testCloseDialog()\">Close</button>\n }\n </div>\n </div>\n </div>\n}\n\n<!-- Log Detail Panel (inlined) -->\n@if (ShowLogDetailPanel && SelectedLog) {\n <div style=\"position:fixed;inset:0;z-index:10000;pointer-events:none\">\n <div style=\"position:absolute;inset:0;background:rgba(0,0,0,0.35);pointer-events:auto\" (click)=\"onLogDetailClose()\"></div>\n <div style=\"position:absolute;top:0;right:0;height:100%;width:560px;max-width:95vw;background:var(--mj-bg-surface,#fff);box-shadow:-4px 0 20px rgba(0,0,0,0.15);display:flex;flex-direction:column;pointer-events:auto\">\n <div style=\"padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);display:flex;align-items:center;justify-content:space-between\">\n <div>\n <h3 style=\"margin:0;font-size:1rem;font-weight:600\">{{ SelectedLog.ToolName }}</h3>\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin-top:4px\">\n <span [class]=\"SelectedLog.Status === 'Success' ? 'status-success' : SelectedLog.Status === 'Error' ? 'status-error' : 'status-running'\" style=\"font-weight:600\">{{ SelectedLog.Status }}</span>\n <span style=\"margin:0 8px\">\u00B7</span>\n <span>{{ formatDate(SelectedLog.StartedAt) }}</span>\n @if (SelectedLog.DurationMs != null) {\n <span style=\"margin:0 8px\">\u00B7</span>\n <span>{{ formatDuration(SelectedLog.DurationMs) }}</span>\n }\n </div>\n </div>\n <button mjButton (click)=\"onLogDetailClose()\" aria-label=\"Close\"><i class=\"fa-solid fa-xmark\"></i></button>\n </div>\n <div style=\"flex:1;overflow-y:auto;padding:16px 20px\">\n <div style=\"margin-bottom:16px\">\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin-bottom:4px\">Connection</div>\n <div style=\"font-size:0.875rem\">{{ SelectedLog.ConnectionName || SelectedLog.ConnectionID }}</div>\n @if (SelectedLog.ServerName) {\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin-top:2px\">{{ SelectedLog.ServerName }}</div>\n }\n </div>\n @if (SelectedLog.InputArgs) {\n <mj-accordion-panel class=\"mcp-log-section\" Size=\"sm\" [FlushBody]=\"true\"\n [Expanded]=\"LogInputArgsExpanded\" (ExpandedChange)=\"LogInputArgsExpanded = $event\">\n <ng-template mjAccordionTitle><span>Input Arguments</span></ng-template>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;overflow-y:auto;max-height:240px;margin:0\">{{ formatLogJson(SelectedLog.InputArgs) }}</pre>\n </mj-accordion-panel>\n }\n @if (SelectedLog.Result) {\n <mj-accordion-panel class=\"mcp-log-section\" Size=\"sm\" [FlushBody]=\"true\"\n [Expanded]=\"LogResultExpanded\" (ExpandedChange)=\"LogResultExpanded = $event\">\n <ng-template mjAccordionTitle><span>Result</span></ng-template>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;overflow-y:auto;max-height:360px;margin:0\">{{ formatLogJson(SelectedLog.Result) }}</pre>\n </mj-accordion-panel>\n }\n @if (SelectedLog.ErrorMessage) {\n <mj-accordion-panel class=\"mcp-log-section\" Size=\"sm\" [FlushBody]=\"true\"\n [Expanded]=\"LogErrorExpanded\" (ExpandedChange)=\"LogErrorExpanded = $event\">\n <ng-template mjAccordionTitle><span style=\"color:var(--mj-status-error-text,#c53030)\">Error</span></ng-template>\n <pre style=\"background:var(--mj-status-error-bg,#fff5f5);border:1px solid var(--mj-status-error-border,#feb2b2);border-radius:4px;padding:12px;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;margin:0;color:var(--mj-status-error-text,#c53030)\">{{ SelectedLog.ErrorMessage }}</pre>\n </mj-accordion-panel>\n }\n </div>\n <div style=\"padding:12px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);display:flex;gap:8px;justify-content:flex-start\">\n @if (SelectedLog.ToolID) {\n <button mjButton variant=\"primary\" (click)=\"onRunAgainFromLog({ toolId: SelectedLog.ToolID!, connectionId: SelectedLog.ConnectionID })\"><i class=\"fa-solid fa-redo\"></i> Run Again</button>\n }\n <button mjButton (click)=\"onLogDetailClose()\">Close</button>\n </div>\n </div>\n </div>\n}\n", styles: ["/* MCP Dashboard - Header + Filter Panel Layout (outer shell now provided by <mj-page-layout>) */\n\n/* Tab navigation now provided by <mj-tab-nav>. */\n\n/* ========================================\n Main Content Area\n ======================================== */\n\n/* ========================================\n Data Display Components\n ======================================== */\n\n/* Data Grid (Cards) */\n.data-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));\n gap: 16px;\n}\n\n/* Empty-state placeholder spans the full grid so it centers instead of\n sitting in a single 350px column. */\n.data-grid > mj-empty-state {\n grid-column: 1 / -1;\n}\n\n.data-card {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n background: var(--mj-bg-surface);\n transition: box-shadow 0.2s ease;\n}\n\n.data-card:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n.card-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.card-title {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.card-title i {\n color: var(--mj-brand-primary);\n}\n\n.card-title .name {\n font-weight: 600;\n font-size: 15px;\n}\n\n.card-actions {\n display: flex;\n gap: 4px;\n}\n\n.card-actions button {\n padding: 4px 8px;\n}\n\n.card-body {\n padding: 16px;\n}\n\n.card-body .description {\n margin: 0 0 12px 0;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.details-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 12px;\n}\n\n.detail {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.detail.full-width {\n grid-column: 1 / -1;\n}\n\n.detail.error .value {\n color: var(--mj-status-error);\n}\n\n.detail .label {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n font-weight: 500;\n}\n\n.detail .value {\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.detail .value.url,\n.detail .value.command {\n font-family: monospace;\n font-size: 12px;\n background: var(--mj-bg-surface-card);\n padding: 4px 8px;\n border-radius: 4px;\n word-break: break-all;\n}\n\n/* Status Badges */\n.status-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n}\n\n.status-active {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.status-inactive {\n background: color-mix(in srgb, var(--mj-text-disabled) 10%, var(--mj-bg-surface));\n color: var(--mj-text-disabled);\n}\n\n.status-error {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.status-deprecated {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.status-unknown {\n background: color-mix(in srgb, var(--mj-text-disabled) 10%, var(--mj-bg-surface));\n color: var(--mj-text-disabled);\n}\n\n/* Sync Progress & Result Banners */\n.sync-progress-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-bottom: 1px solid color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n color: var(--mj-brand-primary);\n font-size: 13px;\n}\n\n.sync-progress-banner i {\n color: var(--mj-brand-primary);\n}\n\n.sync-progress-banner .sync-message {\n flex: 1;\n}\n\n/* Data Table */\n.data-table {\n overflow: auto;\n background: var(--mj-bg-surface);\n border-radius: 8px;\n border: 1px solid var(--mj-border-default);\n}\n\n.data-table table {\n width: 100%;\n border-collapse: collapse;\n}\n\n.data-table th,\n.data-table td {\n padding: 12px 16px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.data-table th {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n font-weight: 600;\n background: var(--mj-bg-surface-card);\n position: sticky;\n top: 0;\n}\n\n.data-table tbody tr {\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.data-table tbody tr:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.data-table tbody tr:last-child td {\n border-bottom: none;\n}\n\n.data-table tbody tr.error-row {\n background: color-mix(in srgb, var(--mj-status-error) 5%, var(--mj-bg-surface));\n}\n\n.data-table .tool-name {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 500;\n}\n\n.data-table .tool-name i {\n color: var(--mj-brand-primary);\n}\n\n.data-table .error-message {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--mj-status-error);\n font-size: 12px;\n}\n\n.data-table .server-name {\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n/* Sortable Column Headers */\n.data-table th.sortable {\n cursor: pointer;\n user-select: none;\n transition: background 0.15s ease;\n}\n\n.data-table th.sortable:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.data-table th.sortable .sort-icon {\n margin-left: 6px;\n font-size: 10px;\n color: var(--mj-border-strong);\n transition: color 0.15s ease;\n}\n\n.data-table th.sortable:hover .sort-icon {\n color: var(--mj-text-disabled);\n}\n\n.data-table th.sorted-asc .sort-icon,\n.data-table th.sorted-desc .sort-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Status badge icons */\n.status-badge i {\n font-size: 12px;\n}\n\n/* ========================================\n View mode toggle now provided by <mj-view-toggle>.\n ======================================== */\n\n/* ========================================\n Tools Tab - Server Groups\n ======================================== */\n.tools-container {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.server-group {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n\n.server-group-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.server-group-header:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.server-group.collapsed .server-group-header {\n border-bottom: none;\n}\n\n.server-info {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.expand-icon {\n font-size: 12px;\n color: var(--mj-text-disabled);\n transition: transform 0.2s ease;\n}\n\n.expand-icon.expanded {\n transform: rotate(90deg);\n}\n\n.server-name {\n font-weight: 600;\n font-size: 15px;\n color: var(--mj-text-primary);\n}\n\n.tool-count {\n font-size: 12px;\n color: var(--mj-text-secondary);\n background: rgba(0, 0, 0, 0.05);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.status-badge.small {\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.server-actions {\n display: flex;\n gap: 4px;\n}\n\n/* ========================================\n Tools Grid (Card View)\n ======================================== */\n.tools-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 12px;\n padding: 16px;\n}\n\n.tool-card {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n transition: all 0.2s ease;\n overflow: hidden;\n}\n\n.tool-card:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.tool-card.expanded {\n border-color: var(--mj-brand-primary);\n}\n\n.tool-card-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n padding: 12px 14px;\n cursor: pointer;\n}\n\n.tool-title {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n}\n\n.tool-title i {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n.tool-title .name {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.tool-meta {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.param-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: var(--mj-bg-surface-card);\n border-radius: 10px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.param-badge i {\n font-size: 10px;\n}\n\n.tool-description {\n margin: 0;\n padding: 0 14px 8px 14px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.tool-card-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 8px 14px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n/* Tool Details (Expanded) */\n.tool-details {\n padding: 12px 14px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n}\n\n.tool-details .detail-row {\n display: flex;\n padding: 6px 0;\n font-size: 12px;\n}\n\n.tool-details .detail-row.full {\n flex-direction: column;\n gap: 6px;\n}\n\n.tool-details .detail-label {\n min-width: 100px;\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.tool-details .detail-value {\n color: var(--mj-text-primary);\n}\n\n.tool-details .detail-value.mono {\n font-family: 'Consolas', 'Monaco', monospace;\n background: var(--mj-bg-surface);\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid var(--mj-border-default);\n}\n\n.schema-preview {\n margin: 0;\n padding: 10px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-family: 'Consolas', 'Monaco', monospace;\n font-size: 11px;\n line-height: 1.4;\n max-height: 200px;\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ========================================\n Tools List View\n ======================================== */\n.tools-list {\n overflow: auto;\n}\n\n.tools-list table {\n width: 100%;\n border-collapse: collapse;\n}\n\n.tools-list th,\n.tools-list td {\n padding: 10px 14px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.tools-list th {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n font-weight: 600;\n background: var(--mj-bg-surface-card);\n position: sticky;\n top: 0;\n}\n\n.tools-list tbody tr {\n transition: background 0.15s ease;\n}\n\n.tools-list tbody tr:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.tools-list tbody tr.expanded {\n background: color-mix(in srgb, var(--mj-brand-primary) 4%, transparent);\n}\n\n.tool-name-cell {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.tool-name-cell i {\n color: var(--mj-brand-primary);\n margin-top: 2px;\n}\n\n.tool-name-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.tool-name-info .tool-title {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.tool-name-info .tool-code {\n font-size: 11px;\n font-family: monospace;\n color: var(--mj-text-secondary);\n}\n\n.description-cell {\n max-width: 300px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.param-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n background: var(--mj-bg-surface-card);\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.actions-cell {\n white-space: nowrap;\n}\n\n/* Expanded Detail Row in List View */\n.detail-row-expanded td {\n padding: 0;\n background: var(--mj-bg-surface-card);\n}\n\n.inline-details {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n padding: 14px;\n border-top: 1px dashed var(--mj-border-default);\n}\n\n.inline-details .detail-section {\n display: flex;\n gap: 8px;\n font-size: 12px;\n}\n\n.inline-details .detail-section.full {\n flex-basis: 100%;\n flex-direction: column;\n gap: 6px;\n}\n\n.inline-details .detail-label {\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.inline-details .detail-value {\n color: var(--mj-text-primary);\n}\n\n.inline-details .detail-value.mono {\n font-family: monospace;\n}\n\n/* ========================================\n Responsive Styles\n ======================================== */\n@media (max-width: 900px) {\n .mcp-dashboard {\n flex-direction: column;\n }\n\n .sidebar {\n width: 100%;\n min-width: 100%;\n flex-direction: row;\n height: auto;\n border-right: none;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .sidebar-header {\n padding: 12px 16px;\n border-bottom: none;\n border-right: 1px solid var(--mj-border-default);\n }\n\n .sidebar-nav {\n flex-direction: row;\n padding: 8px;\n overflow-x: auto;\n flex: 1;\n }\n\n .nav-item {\n padding: 8px 12px;\n margin-bottom: 0;\n margin-right: 4px;\n white-space: nowrap;\n }\n\n .sidebar-stats {\n display: none;\n }\n\n .sidebar-footer {\n border-top: none;\n border-left: 1px solid var(--mj-border-default);\n padding: 8px;\n }\n\n .data-grid {\n grid-template-columns: 1fr;\n }\n\n .header-actions {\n flex-wrap: wrap;\n }\n\n .search-box {\n min-width: 100%;\n order: 1;\n }\n}\n\n@media (max-width: 600px) {\n .content-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n\n .header-actions {\n width: 100%;\n }\n\n .search-box {\n width: 100%;\n }\n}\n\n/* ========================================\n Clickable Log Rows\n ======================================== */\n.clickable-row {\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.clickable-row:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent) !important;\n}\n\n.clickable-row .action-cell {\n color: var(--mj-border-strong);\n transition: color 0.15s ease;\n}\n\n.clickable-row:hover .action-cell {\n color: var(--mj-brand-primary);\n}\n\n.action-cell {\n width: 40px;\n text-align: center;\n}\n\n/* ========================================\n Expandable Card Styles\n ======================================== */\n.card-header.clickable {\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.card-header.clickable:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.expand-arrow {\n font-size: 12px;\n color: var(--mj-text-disabled);\n transition: transform 0.2s ease;\n margin-right: 4px;\n}\n\n.expand-arrow.rotated {\n transform: rotate(90deg);\n}\n\n.data-card.expanded {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n/* ========================================\n Expanded Tools Section (Server/Connection Cards)\n ======================================== */\n.expanded-tools-section {\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.tools-section-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.tools-section-header i {\n font-size: 14px;\n}\n\n.no-tools-message {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.no-tools-message i {\n color: var(--mj-text-disabled);\n}\n\n/* Tools Mini List */\n.tools-mini-list {\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: 300px;\n overflow-y: auto;\n}\n\n.tool-mini-card {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n transition: all 0.15s ease;\n}\n\n.tool-mini-card:hover {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 2%, transparent);\n}\n\n.tool-mini-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n}\n\n.tool-mini-name {\n font-weight: 500;\n font-size: 13px;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tool-mini-params {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.tool-mini-params i {\n font-size: 10px;\n}\n\n.tool-mini-card button {\n flex-shrink: 0;\n margin-left: 8px;\n}\n\n/* Search Highlight */\n:host ::ng-deep .search-highlight,\n.search-highlight {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: inherit;\n}\n"] }]
|
|
5069
|
-
}], () => [{ type: i0.ChangeDetectorRef }, { type: i1.MCPToolsService }], null); })();
|
|
5070
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MCPDashboardComponent, { className: "MCPDashboardComponent", filePath: "src/MCP/mcp-dashboard.component.ts", lineNumber:
|
|
5087
|
+
}], () => [{ type: i0.ChangeDetectorRef }, { type: i1.MCPToolsService }, { type: i2.MJConfirmService }], null); })();
|
|
5088
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MCPDashboardComponent, { className: "MCPDashboardComponent", filePath: "src/MCP/mcp-dashboard.component.ts", lineNumber: 258 }); })();
|
|
5071
5089
|
//# sourceMappingURL=mcp-dashboard.component.js.map
|