@memberjunction/ng-dashboards 5.11.0 → 5.12.0
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/agents/agent-configuration.component.d.ts +34 -2
- package/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-configuration.component.js +586 -223
- package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.js +2 -2
- package/dist/AI/components/agents/agent-filter-panel.component.d.ts +8 -0
- package/dist/AI/components/agents/agent-filter-panel.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-filter-panel.component.js +85 -52
- package/dist/AI/components/agents/agent-filter-panel.component.js.map +1 -1
- package/dist/AI/components/charts/performance-heatmap.component.d.ts +1 -0
- package/dist/AI/components/charts/performance-heatmap.component.d.ts.map +1 -1
- package/dist/AI/components/charts/performance-heatmap.component.js +27 -5
- package/dist/AI/components/charts/performance-heatmap.component.js.map +1 -1
- package/dist/AI/components/charts/time-series-chart.component.d.ts +5 -0
- package/dist/AI/components/charts/time-series-chart.component.d.ts.map +1 -1
- package/dist/AI/components/charts/time-series-chart.component.js +23 -8
- package/dist/AI/components/charts/time-series-chart.component.js.map +1 -1
- package/dist/AI/components/execution-monitoring.component.js +2 -2
- package/dist/AI/components/execution-monitoring.component.js.map +1 -1
- package/dist/AI/components/models/model-management.component.js +2 -2
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js +2 -2
- package/dist/AI/components/prompts/prompt-filter-panel.component.js +2 -2
- package/dist/AI/components/prompts/prompt-management.component.js +3 -3
- package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-version-control.component.js +2 -2
- package/dist/AI/components/requests/agent-requests-resource.component.d.ts +83 -0
- package/dist/AI/components/requests/agent-requests-resource.component.d.ts.map +1 -0
- package/dist/AI/components/requests/agent-requests-resource.component.js +547 -0
- package/dist/AI/components/requests/agent-requests-resource.component.js.map +1 -0
- package/dist/AI/components/system/system-config-filter-panel.component.js +2 -2
- package/dist/AI/components/system/system-configuration.component.js +2 -2
- package/dist/AI/components/widgets/kpi-card.component.js +7 -7
- package/dist/AI/components/widgets/kpi-card.component.js.map +1 -1
- package/dist/AI/components/widgets/live-execution-widget.component.d.ts.map +1 -1
- package/dist/AI/components/widgets/live-execution-widget.component.js +6 -6
- package/dist/AI/components/widgets/live-execution-widget.component.js.map +1 -1
- package/dist/AI/index.d.ts +1 -0
- package/dist/AI/index.d.ts.map +1 -1
- package/dist/AI/index.js +2 -0
- package/dist/AI/index.js.map +1 -1
- package/dist/APIKeys/api-applications-panel.component.js +3 -3
- package/dist/APIKeys/api-applications-panel.component.js.map +1 -1
- package/dist/APIKeys/api-key-create-dialog.component.js +3 -3
- package/dist/APIKeys/api-key-create-dialog.component.js.map +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.js +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.js.map +1 -1
- package/dist/APIKeys/api-key-list.component.js +3 -3
- package/dist/APIKeys/api-key-list.component.js.map +1 -1
- package/dist/APIKeys/api-keys-resource.component.js +1 -1
- package/dist/APIKeys/api-keys-resource.component.js.map +1 -1
- package/dist/APIKeys/api-scopes-panel.component.js +2 -2
- package/dist/APIKeys/api-usage-panel.component.js +2 -2
- package/dist/Actions/components/actions-overview.component.js +2 -2
- package/dist/Actions/components/execution-monitoring.component.js +2 -2
- package/dist/Actions/components/explorer/action-breadcrumb.component.js +2 -2
- package/dist/Actions/components/explorer/action-card.component.js +2 -2
- package/dist/Actions/components/explorer/action-explorer.component.js +2 -2
- package/dist/Actions/components/explorer/action-list-item.component.js +2 -2
- package/dist/Actions/components/explorer/action-toolbar.component.js +2 -2
- package/dist/Actions/components/explorer/action-tree-panel.component.js +2 -2
- package/dist/Actions/components/explorer/new-action-panel.component.js +2 -2
- package/dist/Actions/components/explorer/new-action-panel.component.js.map +1 -1
- package/dist/Actions/components/explorer/new-category-panel.component.js +2 -2
- package/dist/Actions/components/explorer/new-category-panel.component.js.map +1 -1
- package/dist/Communication/communication-dashboard.component.js +2 -2
- package/dist/Communication/communication-logs-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-logs-resource.component.js +3 -3
- package/dist/Communication/communication-logs-resource.component.js.map +1 -1
- package/dist/Communication/communication-monitor-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-monitor-resource.component.js +5 -5
- package/dist/Communication/communication-monitor-resource.component.js.map +1 -1
- package/dist/Communication/communication-providers-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-providers-resource.component.js +3 -3
- package/dist/Communication/communication-providers-resource.component.js.map +1 -1
- package/dist/Communication/communication-runs-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-runs-resource.component.js +3 -3
- package/dist/Communication/communication-runs-resource.component.js.map +1 -1
- package/dist/Communication/communication-templates-resource.component.js +2 -2
- package/dist/Communication/communication-templates-resource.component.js.map +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.js +2 -2
- package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js +2 -2
- package/dist/ComponentStudio/components/artifact-load-dialog.component.js +2 -2
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js +2 -2
- package/dist/ComponentStudio/components/browser/component-browser.component.js +2 -2
- package/dist/ComponentStudio/components/editors/code-editor-panel.component.js +2 -2
- package/dist/ComponentStudio/components/editors/code-editor-panel.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js +2 -2
- package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/requirements-editor.component.js +2 -2
- package/dist/ComponentStudio/components/editors/requirements-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/spec-editor.component.js +2 -2
- package/dist/ComponentStudio/components/editors/spec-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.js +2 -2
- package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js +2 -2
- package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/text-import-dialog.component.js +2 -2
- package/dist/ComponentStudio/components/text-import-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/workspace/component-preview.component.js +2 -2
- package/dist/ComponentStudio/components/workspace/editor-tabs.component.js +2 -2
- package/dist/ComponentStudio/components/workspace/editor-tabs.component.js.map +1 -1
- package/dist/Credentials/components/credentials-audit-resource.component.js +9 -9
- package/dist/Credentials/components/credentials-audit-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-categories-resource.component.d.ts.map +1 -1
- package/dist/Credentials/components/credentials-categories-resource.component.js +11 -3
- package/dist/Credentials/components/credentials-categories-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.js +2 -2
- package/dist/Credentials/components/credentials-overview-resource.component.d.ts.map +1 -1
- package/dist/Credentials/components/credentials-overview-resource.component.js +12 -11
- package/dist/Credentials/components/credentials-overview-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-types-resource.component.js +9 -9
- package/dist/Credentials/components/credentials-types-resource.component.js.map +1 -1
- package/dist/Credentials/credentials-dashboard.component.js +2 -2
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js +2 -2
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js +2 -2
- package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.js +2 -2
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js +2 -2
- package/dist/DataExplorer/components/view-selector/view-selector.component.js +2 -2
- package/dist/DataExplorer/data-explorer-dashboard.component.js +4 -4
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/Home/home-dashboard.component.js +2 -2
- package/dist/Integration/components/activity/activity.component.d.ts +1 -1
- package/dist/Integration/components/activity/activity.component.d.ts.map +1 -1
- package/dist/Integration/components/activity/activity.component.js +5 -5
- package/dist/Integration/components/activity/activity.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.d.ts +31 -2
- package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +753 -412
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js +3 -3
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js.map +1 -1
- package/dist/Integration/components/overview/overview.component.d.ts +0 -1
- package/dist/Integration/components/overview/overview.component.d.ts.map +1 -1
- package/dist/Integration/components/overview/overview.component.js +3 -6
- package/dist/Integration/components/overview/overview.component.js.map +1 -1
- package/dist/Integration/components/pipelines/pipelines.component.js +3 -3
- package/dist/Integration/components/pipelines/pipelines.component.js.map +1 -1
- package/dist/Integration/components/schedules/schedules.component.d.ts +20 -0
- package/dist/Integration/components/schedules/schedules.component.d.ts.map +1 -1
- package/dist/Integration/components/schedules/schedules.component.js +97 -5
- package/dist/Integration/components/schedules/schedules.component.js.map +1 -1
- package/dist/Integration/components/visual-editor/visual-editor.component.js +2 -2
- package/dist/Integration/components/widgets/integration-card.component.d.ts.map +1 -1
- package/dist/Integration/components/widgets/integration-card.component.js +5 -1
- package/dist/Integration/components/widgets/integration-card.component.js.map +1 -1
- package/dist/Integration/components/widgets/run-history-panel.component.js +2 -2
- package/dist/Integration/components/widgets/run-history-panel.component.js.map +1 -1
- package/dist/Integration/integration.module.d.ts +2 -1
- package/dist/Integration/integration.module.d.ts.map +1 -1
- package/dist/Integration/integration.module.js +7 -3
- package/dist/Integration/integration.module.js.map +1 -1
- package/dist/Integration/services/integration-data.service.d.ts +27 -2
- package/dist/Integration/services/integration-data.service.d.ts.map +1 -1
- package/dist/Integration/services/integration-data.service.js +107 -4
- package/dist/Integration/services/integration-data.service.js.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.js +2 -2
- package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-categories-resource.component.js +2 -2
- package/dist/Lists/components/lists-categories-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.js +2 -2
- package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.js +2 -2
- package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
- package/dist/Lists/components/venn-diagram/venn-diagram.component.js +3 -3
- package/dist/Lists/components/venn-diagram/venn-diagram.component.js.map +1 -1
- package/dist/MCP/components/mcp-connection-dialog.component.js +2 -2
- package/dist/MCP/components/mcp-log-detail-panel.component.js +2 -2
- package/dist/MCP/components/mcp-log-detail-panel.component.js.map +1 -1
- package/dist/MCP/components/mcp-server-dialog.component.js +2 -2
- package/dist/MCP/components/mcp-test-tool-dialog.component.js +2 -2
- package/dist/MCP/components/mcp-test-tool-dialog.component.js.map +1 -1
- package/dist/MCP/mcp-dashboard.component.js +2 -2
- package/dist/MCP/mcp-filter-panel.component.js +2 -2
- package/dist/QueryBrowser/query-browser-resource.component.js +7 -7
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/Scheduling/components/index.d.ts +0 -1
- package/dist/Scheduling/components/index.d.ts.map +1 -1
- package/dist/Scheduling/components/index.js +0 -1
- package/dist/Scheduling/components/index.js.map +1 -1
- package/dist/Scheduling/components/scheduling-activity.component.js +2 -2
- package/dist/Scheduling/components/scheduling-jobs.component.d.ts +6 -9
- package/dist/Scheduling/components/scheduling-jobs.component.d.ts.map +1 -1
- package/dist/Scheduling/components/scheduling-jobs.component.js +118 -110
- package/dist/Scheduling/components/scheduling-jobs.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-overview.component.js +3 -3
- package/dist/Scheduling/components/scheduling-overview.component.js.map +1 -1
- package/dist/Scheduling/scheduling-dashboard.component.js +2 -2
- package/dist/SystemDiagnostics/system-diagnostics.component.js +4 -4
- package/dist/SystemDiagnostics/system-diagnostics.component.js.map +1 -1
- package/dist/Testing/components/testing-analytics.component.js +2 -2
- package/dist/Testing/components/testing-analytics.component.js.map +1 -1
- package/dist/Testing/components/testing-dashboard-tab.component.js +4 -4
- package/dist/Testing/components/testing-dashboard-tab.component.js.map +1 -1
- package/dist/Testing/components/testing-explorer.component.js +2 -2
- package/dist/Testing/components/testing-explorer.component.js.map +1 -1
- package/dist/Testing/components/testing-review.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-review.component.js +5 -5
- package/dist/Testing/components/testing-review.component.js.map +1 -1
- package/dist/Testing/components/testing-runs.component.js +2 -2
- package/dist/Testing/components/testing-runs.component.js.map +1 -1
- package/dist/Testing/components/widgets/oracle-breakdown-table.component.js +2 -2
- package/dist/Testing/components/widgets/oracle-breakdown-table.component.js.map +1 -1
- package/dist/Testing/components/widgets/suite-tree.component.js +4 -4
- package/dist/Testing/components/widgets/suite-tree.component.js.map +1 -1
- package/dist/Testing/components/widgets/test-run-detail-panel.component.js +2 -2
- package/dist/Testing/components/widgets/test-run-detail-panel.component.js.map +1 -1
- package/dist/Testing/testing-dashboard.component.js +2 -2
- package/dist/VersionHistory/components/diff-resource.component.js +2 -2
- package/dist/VersionHistory/components/graph-resource.component.js +2 -2
- package/dist/VersionHistory/components/labels-resource.component.js +3 -3
- package/dist/VersionHistory/components/labels-resource.component.js.map +1 -1
- package/dist/VersionHistory/components/restore-resource.component.js +3 -3
- package/dist/VersionHistory/components/restore-resource.component.js.map +1 -1
- package/dist/__tests__/integration-data-service.test.js +1 -0
- package/dist/__tests__/integration-data-service.test.js.map +1 -1
- package/dist/module.d.ts +52 -49
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +25 -6
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +1 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -1
- package/dist/public-api.js.map +1 -1
- package/package.json +42 -40
- package/dist/Scheduling/components/job-slideout.component.d.ts +0 -45
- package/dist/Scheduling/components/job-slideout.component.d.ts.map +0 -1
- package/dist/Scheduling/components/job-slideout.component.js +0 -459
- package/dist/Scheduling/components/job-slideout.component.js.map +0 -1
|
@@ -2751,7 +2751,7 @@ let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
|
|
|
2751
2751
|
i0.ɵɵconditional(ctx.ShowTestToolDialog ? 53 : -1);
|
|
2752
2752
|
i0.ɵɵadvance();
|
|
2753
2753
|
i0.ɵɵconditional(ctx.ShowLogDetailPanel && ctx.SelectedLog ? 54 : -1);
|
|
2754
|
-
} }, styles: ["\n\n.mcp-dashboard[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-background-color, #f5f5f5);\n overflow: hidden;\n}\n\n\n\n\n\n.dashboard-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 20px;\n background: white;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n gap: 16px;\n}\n\n.header-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.dashboard-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-color, #333);\n}\n\n.dashboard-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-primary-color, #2196f3);\n font-size: 20px;\n}\n\n.filter-toggle-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: #f5f5f5;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-secondary-color, #666);\n transition: all 0.2s ease;\n}\n\n.filter-toggle-btn[_ngcontent-%COMP%]:hover {\n background: #e8e8e8;\n border-color: #d0d0d0;\n color: var(--mj-text-color, #333);\n}\n\n.filter-toggle-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n.item-count[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-secondary-color, #666);\n padding: 4px 10px;\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n.header-controls[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n\n\n.tab-nav[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n background: #f5f5f5;\n padding: 4px;\n border-radius: 8px;\n}\n\n.tab-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n background: transparent;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary-color, #666);\n transition: all 0.2s ease;\n}\n\n.tab-btn[_ngcontent-%COMP%]:hover {\n background: rgba(255, 255, 255, 0.7);\n color: var(--mj-text-color, #333);\n}\n\n.tab-btn.active[_ngcontent-%COMP%] {\n background: white;\n color: var(--mj-primary-color, #2196f3);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n.tab-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.tab-label[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.tab-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 22px;\n height: 18px;\n padding: 0 6px;\n background: #e0e0e0;\n border-radius: 9px;\n font-size: 11px;\n font-weight: 600;\n color: #666;\n}\n\n.tab-btn.active[_ngcontent-%COMP%] .tab-badge[_ngcontent-%COMP%] {\n background: rgba(33, 150, 243, 0.15);\n color: var(--mj-primary-color, #2196f3);\n}\n\n.tab-badge.has-errors[_ngcontent-%COMP%] {\n background: #f44336;\n color: white;\n}\n\n\n\n\n\n.main-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: row;\n flex: 1;\n overflow: hidden;\n min-width: 0;\n}\n\n\n\n.filter-panel-container[_ngcontent-%COMP%] {\n display: flex;\n flex-shrink: 0;\n width: 280px;\n min-width: 200px;\n max-width: 400px;\n background: white;\n border-right: 1px solid #e0e0e0;\n position: relative;\n transition: width 0.2s ease, margin 0.2s ease;\n}\n\n.filter-panel-container.hidden[_ngcontent-%COMP%] {\n width: 0;\n min-width: 0;\n margin-left: -1px;\n overflow: hidden;\n}\n\n\n\n.resize-handle[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n right: 0;\n width: 4px;\n height: 100%;\n cursor: col-resize;\n background: transparent;\n transition: background 0.2s ease;\n z-index: 10;\n}\n\n.resize-handle[_ngcontent-%COMP%]:hover, \n.resize-handle[_ngcontent-%COMP%]:active {\n background: var(--mj-primary-color, #2196f3);\n}\n\n\n\n.content-area[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow: hidden;\n min-width: 0;\n}\n\n\n\n.content-body[_ngcontent-%COMP%] {\n flex: 1;\n padding: 20px 24px;\n overflow: auto;\n}\n\n\n\n.error-banner[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n margin: 16px 24px 0 24px;\n background: #ffebee;\n border: 1px solid #f44336;\n border-radius: 8px;\n color: #c62828;\n}\n\n.error-banner[_ngcontent-%COMP%] .close-btn[_ngcontent-%COMP%] {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: #c62828;\n padding: 4px;\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.data-card[_ngcontent-%COMP%] {\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n background: white;\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: #fafafa;\n border-bottom: 1px solid #e0e0e0;\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-primary-color, #2196f3);\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-color, #666);\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: #f44336;\n}\n\n.detail[_ngcontent-%COMP%] .label[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n color: #999;\n font-weight: 500;\n}\n\n.detail[_ngcontent-%COMP%] .value[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-color, #333);\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: #f5f5f5;\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: rgba(76, 175, 80, 0.1);\n color: #4caf50;\n}\n\n.status-inactive[_ngcontent-%COMP%] {\n background: rgba(158, 158, 158, 0.1);\n color: #9e9e9e;\n}\n\n.status-error[_ngcontent-%COMP%] {\n background: rgba(244, 67, 54, 0.1);\n color: #f44336;\n}\n\n.status-deprecated[_ngcontent-%COMP%] {\n background: rgba(255, 152, 0, 0.1);\n color: #ff9800;\n}\n\n.status-unknown[_ngcontent-%COMP%] {\n background: rgba(158, 158, 158, 0.1);\n color: #9e9e9e;\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: linear-gradient(90deg, rgba(33, 150, 243, 0.1), rgba(33, 150, 243, 0.05));\n border-bottom: 1px solid rgba(33, 150, 243, 0.2);\n color: #1976d2;\n font-size: 13px;\n}\n\n.sync-progress-banner[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #2196f3;\n}\n\n.sync-progress-banner[_ngcontent-%COMP%] .sync-message[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.sync-result-banner[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n font-size: 12px;\n border-bottom: 1px solid;\n}\n\n.sync-result-banner.success[_ngcontent-%COMP%] {\n background: rgba(76, 175, 80, 0.08);\n border-color: rgba(76, 175, 80, 0.2);\n color: #388e3c;\n}\n\n.sync-result-banner.success[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #4caf50;\n}\n\n.sync-result-banner.error[_ngcontent-%COMP%] {\n background: rgba(244, 67, 54, 0.08);\n border-color: rgba(244, 67, 54, 0.2);\n color: #d32f2f;\n}\n\n.sync-result-banner.error[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #f44336;\n}\n\n\n\n.data-table[_ngcontent-%COMP%] {\n overflow: auto;\n background: white;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\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 #e0e0e0;\n}\n\n.data-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n color: #999;\n font-weight: 600;\n background: #fafafa;\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: #f5f5f5;\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: rgba(244, 67, 54, 0.05);\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-primary-color, #2196f3);\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: #f44336;\n font-size: 12px;\n}\n\n.data-table[_ngcontent-%COMP%] .server-name[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary-color, #666);\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: #f0f0f0;\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: #ccc;\n transition: color 0.15s ease;\n}\n\n.data-table[_ngcontent-%COMP%] th.sortable[_ngcontent-%COMP%]:hover .sort-icon[_ngcontent-%COMP%] {\n color: #999;\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-primary-color, #2196f3);\n}\n\n\n\n.status-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n text-align: center;\n background: white;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n color: #e0e0e0;\n margin-bottom: 16px;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 16px;\n color: var(--mj-text-color, #333);\n}\n\n.empty-state[_ngcontent-%COMP%] .hint[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-secondary-color, #666);\n margin-bottom: 16px;\n}\n\n\n\n\n\n.view-toggle[_ngcontent-%COMP%] {\n display: flex;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n overflow: hidden;\n}\n\n.view-toggle[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n border-radius: 0;\n border: none;\n padding: 6px 10px;\n}\n\n.view-toggle[_ngcontent-%COMP%] button.active[_ngcontent-%COMP%] {\n background: var(--mj-primary-color, #2196f3);\n color: white;\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: white;\n border: 1px solid #e0e0e0;\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: linear-gradient(135deg, #fafafa, #f5f5f5);\n border-bottom: 1px solid #e0e0e0;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.server-group-header[_ngcontent-%COMP%]:hover {\n background: linear-gradient(135deg, #f5f5f5, #efefef);\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: #999;\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-color, #333);\n}\n\n.tool-count[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-secondary-color, #666);\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 #e8e8e8;\n border-radius: 8px;\n background: white;\n transition: all 0.2s ease;\n overflow: hidden;\n}\n\n.tool-card[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-primary-color, #2196f3);\n box-shadow: 0 2px 8px rgba(33, 150, 243, 0.15);\n}\n\n.tool-card.expanded[_ngcontent-%COMP%] {\n border-color: var(--mj-primary-color, #2196f3);\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-primary-color, #2196f3);\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-color, #333);\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: #f5f5f5;\n border-radius: 10px;\n font-size: 11px;\n color: var(--mj-text-secondary-color, #666);\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-color, #666);\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 #f0f0f0;\n background: #fafafa;\n}\n\n\n\n.tool-details[_ngcontent-%COMP%] {\n padding: 12px 14px;\n background: #fafafa;\n border-top: 1px solid #e8e8e8;\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-color, #666);\n font-weight: 500;\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-value[_ngcontent-%COMP%] {\n color: var(--mj-text-color, #333);\n}\n\n.tool-details[_ngcontent-%COMP%] .detail-value.mono[_ngcontent-%COMP%] {\n font-family: 'Consolas', 'Monaco', monospace;\n background: #fff;\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid #e0e0e0;\n}\n\n.schema-preview[_ngcontent-%COMP%] {\n margin: 0;\n padding: 10px 12px;\n background: #fff;\n border: 1px solid #e0e0e0;\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 #f0f0f0;\n}\n\n.tools-list[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n color: #999;\n font-weight: 600;\n background: #fafafa;\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: #fafafa;\n}\n\n.tools-list[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr.expanded[_ngcontent-%COMP%] {\n background: rgba(33, 150, 243, 0.04);\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-primary-color, #2196f3);\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-color, #333);\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-color, #666);\n}\n\n.description-cell[_ngcontent-%COMP%] {\n max-width: 300px;\n font-size: 12px;\n color: var(--mj-text-secondary-color, #666);\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: #f5f5f5;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary-color, #666);\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: #fafafa;\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 #e0e0e0;\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-color, #666);\n font-weight: 500;\n}\n\n.inline-details[_ngcontent-%COMP%] .detail-value[_ngcontent-%COMP%] {\n color: var(--mj-text-color, #333);\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 #e0e0e0;\n }\n\n .sidebar-header[_ngcontent-%COMP%] {\n padding: 12px 16px;\n border-bottom: none;\n border-right: 1px solid #e0e0e0;\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 #e0e0e0;\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: rgba(33, 150, 243, 0.05) !important;\n}\n\n.clickable-row[_ngcontent-%COMP%] .action-cell[_ngcontent-%COMP%] {\n color: #ccc;\n transition: color 0.15s ease;\n}\n\n.clickable-row[_ngcontent-%COMP%]:hover .action-cell[_ngcontent-%COMP%] {\n color: var(--mj-primary-color, #2196f3);\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: #f0f0f0;\n}\n\n.expand-arrow[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #999;\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-primary-color, #2196f3);\n box-shadow: 0 2px 8px rgba(33, 150, 243, 0.15);\n}\n\n\n\n\n\n.expanded-tools-section[_ngcontent-%COMP%] {\n border-top: 1px solid #e0e0e0;\n background: linear-gradient(180deg, #fafafa, #f5f5f5);\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-primary-color, #2196f3);\n border-bottom: 1px solid #e8e8e8;\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-color, #666);\n font-size: 13px;\n}\n\n.no-tools-message[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #999;\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: white;\n border: 1px solid #e8e8e8;\n border-radius: 6px;\n transition: all 0.15s ease;\n}\n\n.tool-mini-card[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-primary-color, #2196f3);\n background: rgba(33, 150, 243, 0.02);\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-color, #333);\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-color, #666);\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: #fef08a;\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: inherit;\n}"] });
|
|
2754
|
+
} }, styles: ["\n\n.mcp-dashboard[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-page);\n overflow: hidden;\n}\n\n\n\n\n\n.dashboard-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 20px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n gap: 16px;\n}\n\n.header-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.dashboard-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.dashboard-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 20px;\n}\n\n.filter-toggle-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-secondary);\n transition: all 0.2s ease;\n}\n\n.filter-toggle-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n}\n\n.filter-toggle-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n.item-count[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-secondary);\n padding: 4px 10px;\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n}\n\n.header-controls[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n\n\n.tab-nav[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-card);\n padding: 4px;\n border-radius: 8px;\n}\n\n.tab-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n background: transparent;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n transition: all 0.2s ease;\n}\n\n.tab-btn[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-bg-surface) 70%, transparent);\n color: var(--mj-text-primary);\n}\n\n.tab-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n.tab-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.tab-label[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.tab-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 22px;\n height: 18px;\n padding: 0 6px;\n background: var(--mj-border-default);\n border-radius: 9px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-muted);\n}\n\n.tab-btn.active[_ngcontent-%COMP%] .tab-badge[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.tab-badge.has-errors[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n\n\n\n\n.main-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: row;\n flex: 1;\n overflow: hidden;\n min-width: 0;\n}\n\n\n\n.filter-panel-container[_ngcontent-%COMP%] {\n display: flex;\n flex-shrink: 0;\n width: 280px;\n min-width: 200px;\n max-width: 400px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n position: relative;\n transition: width 0.2s ease, margin 0.2s ease;\n}\n\n.filter-panel-container.hidden[_ngcontent-%COMP%] {\n width: 0;\n min-width: 0;\n margin-left: -1px;\n overflow: hidden;\n}\n\n\n\n.resize-handle[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n right: 0;\n width: 4px;\n height: 100%;\n cursor: col-resize;\n background: transparent;\n transition: background 0.2s ease;\n z-index: 10;\n}\n\n.resize-handle[_ngcontent-%COMP%]:hover, \n.resize-handle[_ngcontent-%COMP%]:active {\n background: var(--mj-brand-primary);\n}\n\n\n\n.content-area[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow: hidden;\n min-width: 0;\n}\n\n\n\n.content-body[_ngcontent-%COMP%] {\n flex: 1;\n padding: 20px 24px;\n overflow: auto;\n}\n\n\n\n.error-banner[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n margin: 16px 24px 0 24px;\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border: 1px solid var(--mj-status-error);\n border-radius: 8px;\n color: var(--mj-status-error);\n}\n\n.error-banner[_ngcontent-%COMP%] .close-btn[_ngcontent-%COMP%] {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-status-error);\n padding: 4px;\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.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.sync-result-banner[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n font-size: 12px;\n border-bottom: 1px solid;\n}\n\n.sync-result-banner.success[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-success) 20%, transparent);\n color: var(--mj-color-success-700);\n}\n\n.sync-result-banner.success[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n}\n\n.sync-result-banner.error[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-error) 20%, transparent);\n color: var(--mj-status-error);\n}\n\n.sync-result-banner.error[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\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.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n text-align: center;\n background: var(--mj-bg-surface);\n border-radius: 8px;\n border: 1px solid var(--mj-border-default);\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n color: var(--mj-border-default);\n margin-bottom: 16px;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 16px;\n color: var(--mj-text-primary);\n}\n\n.empty-state[_ngcontent-%COMP%] .hint[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-secondary);\n margin-bottom: 16px;\n}\n\n\n\n\n\n.view-toggle[_ngcontent-%COMP%] {\n display: flex;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n}\n\n.view-toggle[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n border-radius: 0;\n border: none;\n padding: 6px 10px;\n}\n\n.view-toggle[_ngcontent-%COMP%] button.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-color-neutral-0);\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}"] });
|
|
2755
2755
|
};
|
|
2756
2756
|
MCPDashboardComponent = __decorate([
|
|
2757
2757
|
RegisterClass(BaseDashboard, 'MCPDashboard')
|
|
@@ -2759,7 +2759,7 @@ MCPDashboardComponent = __decorate([
|
|
|
2759
2759
|
export { MCPDashboardComponent };
|
|
2760
2760
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MCPDashboardComponent, [{
|
|
2761
2761
|
type: Component,
|
|
2762
|
-
args: [{ standalone: false, selector: 'mj-mcp-dashboard', template: "<div class=\"mcp-dashboard\">\n <!-- Header -->\n <div class=\"dashboard-header\">\n <div class=\"header-info\">\n <h2 class=\"dashboard-title\">\n <i class=\"fa-solid fa-plug-circle-bolt\"></i>\n MCP Dashboard\n </h2>\n <button\n type=\"button\"\n class=\"filter-toggle-btn\"\n (click)=\"toggleFilterPanel()\"\n title=\"Toggle Filters\">\n <i class=\"fa-solid fa-filter\"></i>\n @if (FilterPanelVisible) {\n Hide Filters\n } @else {\n Show Filters\n }\n </button>\n <span class=\"item-count\">{{ CurrentFilteredCount }} of {{ CurrentTotalCount }} items</span>\n </div>\n\n <div class=\"header-controls\">\n <!-- Tab Navigation -->\n <div class=\"tab-nav\">\n <button\n type=\"button\"\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'servers'\"\n (click)=\"setActiveTab('servers')\">\n <i class=\"fa-solid fa-server\"></i>\n <span class=\"tab-label\">Servers</span>\n <span class=\"tab-badge\">{{ servers.length }}</span>\n </button>\n <button\n type=\"button\"\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'connections'\"\n (click)=\"setActiveTab('connections')\">\n <i class=\"fa-solid fa-link\"></i>\n <span class=\"tab-label\">Connections</span>\n <span class=\"tab-badge\">{{ connections.length }}</span>\n </button>\n <button\n type=\"button\"\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'tools'\"\n (click)=\"setActiveTab('tools')\">\n <i class=\"fa-solid fa-wrench\"></i>\n <span class=\"tab-label\">Tools</span>\n <span class=\"tab-badge\">{{ tools.length }}</span>\n </button>\n <button\n type=\"button\"\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'logs'\"\n (click)=\"setActiveTab('logs')\">\n <i class=\"fa-solid fa-list-check\"></i>\n <span class=\"tab-label\">Logs</span>\n <span class=\"tab-badge\" [class.has-errors]=\"stats.failedExecutions > 0\">{{ executionLogs.length }}</span>\n </button>\n </div>\n\n <!-- Action Buttons based on tab -->\n @switch (ActiveTab) {\n @case ('servers') {\n <button kendoButton themeColor=\"primary\" (click)=\"createServer()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Server\n </button>\n }\n @case ('connections') {\n <button kendoButton themeColor=\"primary\" (click)=\"createConnection()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Connection\n </button>\n }\n @case ('tools') {\n <!-- View Mode Toggle -->\n <div class=\"view-toggle\">\n <button\n type=\"button\"\n class=\"view-btn\"\n [class.active]=\"ToolsViewMode === 'card'\"\n (click)=\"setToolsViewMode('card')\"\n title=\"Card View\">\n <i class=\"fa-solid fa-grip\"></i>\n </button>\n <button\n type=\"button\"\n class=\"view-btn\"\n [class.active]=\"ToolsViewMode === 'list'\"\n (click)=\"setToolsViewMode('list')\"\n title=\"List View\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n </div>\n <button kendoButton themeColor=\"primary\" (click)=\"openTestToolDialog()\">\n <i class=\"fa-solid fa-play\"></i>\n Test Tool\n </button>\n }\n @case ('logs') {\n <button kendoButton fillMode=\"flat\" (click)=\"loadAllData()\" [disabled]=\"IsLoading\" title=\"Refresh\">\n <i class=\"fa-solid fa-sync\" [class.fa-spin]=\"IsLoading\"></i>\n Refresh\n </button>\n }\n }\n </div>\n </div>\n\n <!-- Main Content with Filter Panel -->\n <div class=\"main-content\">\n <!-- Filter Panel (Left) -->\n <div class=\"filter-panel-container\" [class.hidden]=\"!FilterPanelVisible\">\n <mj-mcp-filter-panel\n [filters]=\"CurrentFilters\"\n [activeTab]=\"ActiveTab\"\n [totalCount]=\"CurrentTotalCount\"\n [filteredCount]=\"CurrentFilteredCount\"\n (filtersChange)=\"onFiltersChange($event)\"\n (closePanel)=\"toggleFilterPanel()\">\n </mj-mcp-filter-panel>\n <!-- Resize Handle -->\n <div class=\"resize-handle\"></div>\n </div>\n\n <!-- Content Area -->\n <div class=\"content-area\">\n <!-- Error Message -->\n @if (ErrorMessage) {\n <div class=\"error-banner\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n {{ ErrorMessage }}\n <button class=\"close-btn\" (click)=\"ErrorMessage = null\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n }\n\n <!-- Content Body -->\n <div class=\"content-body\">\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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-server\"></i>\n <p>No MCP servers configured</p>\n <button kendoButton themeColor=\"primary\" (click)=\"createServer()\">\n Add Your First Server\n </button>\n </div>\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\" [innerHTML]=\"server.Name | highlightSearch:(filters$ | async)?.searchTerm\"></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 kendoButton fillMode=\"flat\" (click)=\"editServer(server)\" title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button kendoButton fillMode=\"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\" [innerHTML]=\"server.Description | highlightSearch:(filters$ | async)?.searchTerm\"></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 kendoButton\n fillMode=\"flat\"\n size=\"small\"\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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-link\"></i>\n <p>No connections configured</p>\n <button kendoButton themeColor=\"primary\" (click)=\"createConnection()\">\n Add Your First Connection\n </button>\n </div>\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\" [innerHTML]=\"conn.Name | highlightSearch:(filters$ | async)?.searchTerm\"></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 kendoButton\n fillMode=\"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 kendoButton fillMode=\"flat\" (click)=\"editConnection(conn)\" title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button kendoButton fillMode=\"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 <div class=\"sync-result-banner\"\n [class.success]=\"getSyncState(conn.ID)?.lastResult?.Success\"\n [class.error]=\"!getSyncState(conn.ID)?.lastResult?.Success\">\n @if (getSyncState(conn.ID)?.lastResult?.Success) {\n <i class=\"fa-solid fa-check-circle\"></i>\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 <i class=\"fa-solid fa-exclamation-circle\"></i>\n <span>{{ getSyncState(conn.ID)?.lastResult?.ErrorMessage }}</span>\n }\n </div>\n }\n <div class=\"card-body\">\n @if (conn.Description) {\n <p class=\"description\" [innerHTML]=\"conn.Description | highlightSearch:(filters$ | async)?.searchTerm\"></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 kendoButton\n fillMode=\"flat\"\n size=\"small\"\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 (ServerGroups.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-wrench\"></i>\n <p>No tools discovered yet</p>\n <span class=\"hint\">Tools are discovered when connections sync with MCP servers</span>\n </div>\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\">{{ group.tools.length }} 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 kendoButton fillMode=\"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\" [innerHTML]=\"(tool.ToolTitle || tool.ToolName) | highlightSearch:(filters$ | async)?.searchTerm\"></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\" [innerHTML]=\"tool.ToolDescription | highlightSearch:(filters$ | async)?.searchTerm\"></p>\n }\n <div class=\"tool-card-actions\">\n <button kendoButton fillMode=\"flat\" size=\"small\" (click)=\"openTestToolDialog(tool)\" title=\"Test this tool\">\n <i class=\"fa-solid fa-play\"></i>\n Test\n </button>\n <button kendoButton fillMode=\"flat\" size=\"small\" (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\" [innerHTML]=\"(tool.ToolTitle || tool.ToolName) | highlightSearch:(filters$ | async)?.searchTerm\"></span>\n @if (tool.ToolTitle) {\n <span class=\"tool-code\">{{ tool.ToolName }}</span>\n }\n </div>\n </td>\n <td class=\"description-cell\" [innerHTML]=\"(tool.ToolDescription || '-') | highlightSearch:(filters$ | async)?.searchTerm\">\n </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 kendoButton fillMode=\"flat\" size=\"small\" (click)=\"openTestToolDialog(tool)\" title=\"Test\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n <button kendoButton fillMode=\"flat\" size=\"small\" (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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-list-check\"></i>\n <p>No recent execution logs</p>\n <span class=\"hint\">Logs appear when tools are executed via MCP connections</span>\n </div>\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 </div>\n </div>\n </div>\n</div>\n\n<!-- Server Dialog -->\n@if (ShowServerDialog) {\n <mj-mcp-server-dialog\n [server]=\"EditingServer\"\n [visible]=\"ShowServerDialog\"\n (close)=\"onServerDialogClose($event)\">\n </mj-mcp-server-dialog>\n}\n\n<!-- Connection Dialog -->\n@if (ShowConnectionDialog) {\n <mj-mcp-connection-dialog\n [connection]=\"EditingConnection\"\n [servers]=\"servers\"\n [visible]=\"ShowConnectionDialog\"\n (close)=\"onConnectionDialogClose($event)\">\n </mj-mcp-connection-dialog>\n}\n\n<!-- Test Tool Dialog -->\n@if (ShowTestToolDialog) {\n <mj-mcp-test-tool-dialog\n [Visible]=\"ShowTestToolDialog\"\n [Servers]=\"servers\"\n [Connections]=\"connections\"\n [Tools]=\"tools\"\n [SelectedServerID]=\"TestToolServerID\"\n [SelectedConnectionID]=\"TestToolConnectionID\"\n [SelectedToolID]=\"TestToolID\"\n (Close)=\"onTestToolDialogClose()\">\n </mj-mcp-test-tool-dialog>\n}\n\n<!-- Log Detail Panel -->\n@if (ShowLogDetailPanel && SelectedLog) {\n <mj-mcp-log-detail-panel\n [Visible]=\"ShowLogDetailPanel\"\n [Log]=\"SelectedLog\"\n (Close)=\"onLogDetailClose()\"\n (RunAgain)=\"onRunAgainFromLog($event)\">\n </mj-mcp-log-detail-panel>\n}\n", styles: ["/* MCP Dashboard - Header + Filter Panel Layout */\n.mcp-dashboard {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-background-color, #f5f5f5);\n overflow: hidden;\n}\n\n/* ========================================\n Dashboard Header\n ======================================== */\n.dashboard-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 20px;\n background: white;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n gap: 16px;\n}\n\n.header-info {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.dashboard-title {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-color, #333);\n}\n\n.dashboard-title i {\n color: var(--mj-primary-color, #2196f3);\n font-size: 20px;\n}\n\n.filter-toggle-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: #f5f5f5;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-secondary-color, #666);\n transition: all 0.2s ease;\n}\n\n.filter-toggle-btn:hover {\n background: #e8e8e8;\n border-color: #d0d0d0;\n color: var(--mj-text-color, #333);\n}\n\n.filter-toggle-btn i {\n font-size: 12px;\n}\n\n.item-count {\n font-size: 13px;\n color: var(--mj-text-secondary-color, #666);\n padding: 4px 10px;\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n.header-controls {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n/* Tab Navigation */\n.tab-nav {\n display: flex;\n gap: 4px;\n background: #f5f5f5;\n padding: 4px;\n border-radius: 8px;\n}\n\n.tab-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n background: transparent;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary-color, #666);\n transition: all 0.2s ease;\n}\n\n.tab-btn:hover {\n background: rgba(255, 255, 255, 0.7);\n color: var(--mj-text-color, #333);\n}\n\n.tab-btn.active {\n background: white;\n color: var(--mj-primary-color, #2196f3);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n.tab-btn i {\n font-size: 14px;\n}\n\n.tab-label {\n font-weight: 500;\n}\n\n.tab-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 22px;\n height: 18px;\n padding: 0 6px;\n background: #e0e0e0;\n border-radius: 9px;\n font-size: 11px;\n font-weight: 600;\n color: #666;\n}\n\n.tab-btn.active .tab-badge {\n background: rgba(33, 150, 243, 0.15);\n color: var(--mj-primary-color, #2196f3);\n}\n\n.tab-badge.has-errors {\n background: #f44336;\n color: white;\n}\n\n/* ========================================\n Main Content Area (with Filter Panel)\n ======================================== */\n.main-content {\n display: flex;\n flex-direction: row;\n flex: 1;\n overflow: hidden;\n min-width: 0;\n}\n\n/* Filter Panel Container */\n.filter-panel-container {\n display: flex;\n flex-shrink: 0;\n width: 280px;\n min-width: 200px;\n max-width: 400px;\n background: white;\n border-right: 1px solid #e0e0e0;\n position: relative;\n transition: width 0.2s ease, margin 0.2s ease;\n}\n\n.filter-panel-container.hidden {\n width: 0;\n min-width: 0;\n margin-left: -1px;\n overflow: hidden;\n}\n\n/* Resize Handle */\n.resize-handle {\n position: absolute;\n top: 0;\n right: 0;\n width: 4px;\n height: 100%;\n cursor: col-resize;\n background: transparent;\n transition: background 0.2s ease;\n z-index: 10;\n}\n\n.resize-handle:hover,\n.resize-handle:active {\n background: var(--mj-primary-color, #2196f3);\n}\n\n/* Content Area */\n.content-area {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow: hidden;\n min-width: 0;\n}\n\n/* Content Body */\n.content-body {\n flex: 1;\n padding: 20px 24px;\n overflow: auto;\n}\n\n/* Error Banner */\n.error-banner {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n margin: 16px 24px 0 24px;\n background: #ffebee;\n border: 1px solid #f44336;\n border-radius: 8px;\n color: #c62828;\n}\n\n.error-banner .close-btn {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: #c62828;\n padding: 4px;\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.data-card {\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n background: white;\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: #fafafa;\n border-bottom: 1px solid #e0e0e0;\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-primary-color, #2196f3);\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-color, #666);\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: #f44336;\n}\n\n.detail .label {\n font-size: 11px;\n text-transform: uppercase;\n color: #999;\n font-weight: 500;\n}\n\n.detail .value {\n font-size: 13px;\n color: var(--mj-text-color, #333);\n}\n\n.detail .value.url,\n.detail .value.command {\n font-family: monospace;\n font-size: 12px;\n background: #f5f5f5;\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: rgba(76, 175, 80, 0.1);\n color: #4caf50;\n}\n\n.status-inactive {\n background: rgba(158, 158, 158, 0.1);\n color: #9e9e9e;\n}\n\n.status-error {\n background: rgba(244, 67, 54, 0.1);\n color: #f44336;\n}\n\n.status-deprecated {\n background: rgba(255, 152, 0, 0.1);\n color: #ff9800;\n}\n\n.status-unknown {\n background: rgba(158, 158, 158, 0.1);\n color: #9e9e9e;\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: linear-gradient(90deg, rgba(33, 150, 243, 0.1), rgba(33, 150, 243, 0.05));\n border-bottom: 1px solid rgba(33, 150, 243, 0.2);\n color: #1976d2;\n font-size: 13px;\n}\n\n.sync-progress-banner i {\n color: #2196f3;\n}\n\n.sync-progress-banner .sync-message {\n flex: 1;\n}\n\n.sync-result-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n font-size: 12px;\n border-bottom: 1px solid;\n}\n\n.sync-result-banner.success {\n background: rgba(76, 175, 80, 0.08);\n border-color: rgba(76, 175, 80, 0.2);\n color: #388e3c;\n}\n\n.sync-result-banner.success i {\n color: #4caf50;\n}\n\n.sync-result-banner.error {\n background: rgba(244, 67, 54, 0.08);\n border-color: rgba(244, 67, 54, 0.2);\n color: #d32f2f;\n}\n\n.sync-result-banner.error i {\n color: #f44336;\n}\n\n/* Data Table */\n.data-table {\n overflow: auto;\n background: white;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\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 #e0e0e0;\n}\n\n.data-table th {\n font-size: 11px;\n text-transform: uppercase;\n color: #999;\n font-weight: 600;\n background: #fafafa;\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: #f5f5f5;\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: rgba(244, 67, 54, 0.05);\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-primary-color, #2196f3);\n}\n\n.data-table .error-message {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: #f44336;\n font-size: 12px;\n}\n\n.data-table .server-name {\n color: var(--mj-text-secondary-color, #666);\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: #f0f0f0;\n}\n\n.data-table th.sortable .sort-icon {\n margin-left: 6px;\n font-size: 10px;\n color: #ccc;\n transition: color 0.15s ease;\n}\n\n.data-table th.sortable:hover .sort-icon {\n color: #999;\n}\n\n.data-table th.sorted-asc .sort-icon,\n.data-table th.sorted-desc .sort-icon {\n color: var(--mj-primary-color, #2196f3);\n}\n\n/* Status badge icons */\n.status-badge i {\n font-size: 12px;\n}\n\n/* Empty State */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n text-align: center;\n background: white;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n}\n\n.empty-state i {\n font-size: 48px;\n color: #e0e0e0;\n margin-bottom: 16px;\n}\n\n.empty-state p {\n margin: 0 0 8px 0;\n font-size: 16px;\n color: var(--mj-text-color, #333);\n}\n\n.empty-state .hint {\n font-size: 13px;\n color: var(--mj-text-secondary-color, #666);\n margin-bottom: 16px;\n}\n\n/* ========================================\n View Mode Toggle\n ======================================== */\n.view-toggle {\n display: flex;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n overflow: hidden;\n}\n\n.view-toggle button {\n border-radius: 0;\n border: none;\n padding: 6px 10px;\n}\n\n.view-toggle button.active {\n background: var(--mj-primary-color, #2196f3);\n color: white;\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: white;\n border: 1px solid #e0e0e0;\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: linear-gradient(135deg, #fafafa, #f5f5f5);\n border-bottom: 1px solid #e0e0e0;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.server-group-header:hover {\n background: linear-gradient(135deg, #f5f5f5, #efefef);\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: #999;\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-color, #333);\n}\n\n.tool-count {\n font-size: 12px;\n color: var(--mj-text-secondary-color, #666);\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 #e8e8e8;\n border-radius: 8px;\n background: white;\n transition: all 0.2s ease;\n overflow: hidden;\n}\n\n.tool-card:hover {\n border-color: var(--mj-primary-color, #2196f3);\n box-shadow: 0 2px 8px rgba(33, 150, 243, 0.15);\n}\n\n.tool-card.expanded {\n border-color: var(--mj-primary-color, #2196f3);\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-primary-color, #2196f3);\n font-size: 14px;\n}\n\n.tool-title .name {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-color, #333);\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: #f5f5f5;\n border-radius: 10px;\n font-size: 11px;\n color: var(--mj-text-secondary-color, #666);\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-color, #666);\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 #f0f0f0;\n background: #fafafa;\n}\n\n/* Tool Details (Expanded) */\n.tool-details {\n padding: 12px 14px;\n background: #fafafa;\n border-top: 1px solid #e8e8e8;\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-color, #666);\n font-weight: 500;\n}\n\n.tool-details .detail-value {\n color: var(--mj-text-color, #333);\n}\n\n.tool-details .detail-value.mono {\n font-family: 'Consolas', 'Monaco', monospace;\n background: #fff;\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid #e0e0e0;\n}\n\n.schema-preview {\n margin: 0;\n padding: 10px 12px;\n background: #fff;\n border: 1px solid #e0e0e0;\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 #f0f0f0;\n}\n\n.tools-list th {\n font-size: 11px;\n text-transform: uppercase;\n color: #999;\n font-weight: 600;\n background: #fafafa;\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: #fafafa;\n}\n\n.tools-list tbody tr.expanded {\n background: rgba(33, 150, 243, 0.04);\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-primary-color, #2196f3);\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-color, #333);\n}\n\n.tool-name-info .tool-code {\n font-size: 11px;\n font-family: monospace;\n color: var(--mj-text-secondary-color, #666);\n}\n\n.description-cell {\n max-width: 300px;\n font-size: 12px;\n color: var(--mj-text-secondary-color, #666);\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: #f5f5f5;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary-color, #666);\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: #fafafa;\n}\n\n.inline-details {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n padding: 14px;\n border-top: 1px dashed #e0e0e0;\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-color, #666);\n font-weight: 500;\n}\n\n.inline-details .detail-value {\n color: var(--mj-text-color, #333);\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 #e0e0e0;\n }\n\n .sidebar-header {\n padding: 12px 16px;\n border-bottom: none;\n border-right: 1px solid #e0e0e0;\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 #e0e0e0;\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: rgba(33, 150, 243, 0.05) !important;\n}\n\n.clickable-row .action-cell {\n color: #ccc;\n transition: color 0.15s ease;\n}\n\n.clickable-row:hover .action-cell {\n color: var(--mj-primary-color, #2196f3);\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: #f0f0f0;\n}\n\n.expand-arrow {\n font-size: 12px;\n color: #999;\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-primary-color, #2196f3);\n box-shadow: 0 2px 8px rgba(33, 150, 243, 0.15);\n}\n\n/* ========================================\n Expanded Tools Section (Server/Connection Cards)\n ======================================== */\n.expanded-tools-section {\n border-top: 1px solid #e0e0e0;\n background: linear-gradient(180deg, #fafafa, #f5f5f5);\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-primary-color, #2196f3);\n border-bottom: 1px solid #e8e8e8;\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-color, #666);\n font-size: 13px;\n}\n\n.no-tools-message i {\n color: #999;\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: white;\n border: 1px solid #e8e8e8;\n border-radius: 6px;\n transition: all 0.15s ease;\n}\n\n.tool-mini-card:hover {\n border-color: var(--mj-primary-color, #2196f3);\n background: rgba(33, 150, 243, 0.02);\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-color, #333);\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-color, #666);\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: #fef08a;\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: inherit;\n}\n"] }]
|
|
2762
|
+
args: [{ standalone: false, selector: 'mj-mcp-dashboard', template: "<div class=\"mcp-dashboard\">\n <!-- Header -->\n <div class=\"dashboard-header\">\n <div class=\"header-info\">\n <h2 class=\"dashboard-title\">\n <i class=\"fa-solid fa-plug-circle-bolt\"></i>\n MCP Dashboard\n </h2>\n <button\n type=\"button\"\n class=\"filter-toggle-btn\"\n (click)=\"toggleFilterPanel()\"\n title=\"Toggle Filters\">\n <i class=\"fa-solid fa-filter\"></i>\n @if (FilterPanelVisible) {\n Hide Filters\n } @else {\n Show Filters\n }\n </button>\n <span class=\"item-count\">{{ CurrentFilteredCount }} of {{ CurrentTotalCount }} items</span>\n </div>\n\n <div class=\"header-controls\">\n <!-- Tab Navigation -->\n <div class=\"tab-nav\">\n <button\n type=\"button\"\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'servers'\"\n (click)=\"setActiveTab('servers')\">\n <i class=\"fa-solid fa-server\"></i>\n <span class=\"tab-label\">Servers</span>\n <span class=\"tab-badge\">{{ servers.length }}</span>\n </button>\n <button\n type=\"button\"\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'connections'\"\n (click)=\"setActiveTab('connections')\">\n <i class=\"fa-solid fa-link\"></i>\n <span class=\"tab-label\">Connections</span>\n <span class=\"tab-badge\">{{ connections.length }}</span>\n </button>\n <button\n type=\"button\"\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'tools'\"\n (click)=\"setActiveTab('tools')\">\n <i class=\"fa-solid fa-wrench\"></i>\n <span class=\"tab-label\">Tools</span>\n <span class=\"tab-badge\">{{ tools.length }}</span>\n </button>\n <button\n type=\"button\"\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'logs'\"\n (click)=\"setActiveTab('logs')\">\n <i class=\"fa-solid fa-list-check\"></i>\n <span class=\"tab-label\">Logs</span>\n <span class=\"tab-badge\" [class.has-errors]=\"stats.failedExecutions > 0\">{{ executionLogs.length }}</span>\n </button>\n </div>\n\n <!-- Action Buttons based on tab -->\n @switch (ActiveTab) {\n @case ('servers') {\n <button kendoButton themeColor=\"primary\" (click)=\"createServer()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Server\n </button>\n }\n @case ('connections') {\n <button kendoButton themeColor=\"primary\" (click)=\"createConnection()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Connection\n </button>\n }\n @case ('tools') {\n <!-- View Mode Toggle -->\n <div class=\"view-toggle\">\n <button\n type=\"button\"\n class=\"view-btn\"\n [class.active]=\"ToolsViewMode === 'card'\"\n (click)=\"setToolsViewMode('card')\"\n title=\"Card View\">\n <i class=\"fa-solid fa-grip\"></i>\n </button>\n <button\n type=\"button\"\n class=\"view-btn\"\n [class.active]=\"ToolsViewMode === 'list'\"\n (click)=\"setToolsViewMode('list')\"\n title=\"List View\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n </div>\n <button kendoButton themeColor=\"primary\" (click)=\"openTestToolDialog()\">\n <i class=\"fa-solid fa-play\"></i>\n Test Tool\n </button>\n }\n @case ('logs') {\n <button kendoButton fillMode=\"flat\" (click)=\"loadAllData()\" [disabled]=\"IsLoading\" title=\"Refresh\">\n <i class=\"fa-solid fa-sync\" [class.fa-spin]=\"IsLoading\"></i>\n Refresh\n </button>\n }\n }\n </div>\n </div>\n\n <!-- Main Content with Filter Panel -->\n <div class=\"main-content\">\n <!-- Filter Panel (Left) -->\n <div class=\"filter-panel-container\" [class.hidden]=\"!FilterPanelVisible\">\n <mj-mcp-filter-panel\n [filters]=\"CurrentFilters\"\n [activeTab]=\"ActiveTab\"\n [totalCount]=\"CurrentTotalCount\"\n [filteredCount]=\"CurrentFilteredCount\"\n (filtersChange)=\"onFiltersChange($event)\"\n (closePanel)=\"toggleFilterPanel()\">\n </mj-mcp-filter-panel>\n <!-- Resize Handle -->\n <div class=\"resize-handle\"></div>\n </div>\n\n <!-- Content Area -->\n <div class=\"content-area\">\n <!-- Error Message -->\n @if (ErrorMessage) {\n <div class=\"error-banner\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n {{ ErrorMessage }}\n <button class=\"close-btn\" (click)=\"ErrorMessage = null\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n }\n\n <!-- Content Body -->\n <div class=\"content-body\">\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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-server\"></i>\n <p>No MCP servers configured</p>\n <button kendoButton themeColor=\"primary\" (click)=\"createServer()\">\n Add Your First Server\n </button>\n </div>\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\" [innerHTML]=\"server.Name | highlightSearch:(filters$ | async)?.searchTerm\"></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 kendoButton fillMode=\"flat\" (click)=\"editServer(server)\" title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button kendoButton fillMode=\"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\" [innerHTML]=\"server.Description | highlightSearch:(filters$ | async)?.searchTerm\"></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 kendoButton\n fillMode=\"flat\"\n size=\"small\"\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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-link\"></i>\n <p>No connections configured</p>\n <button kendoButton themeColor=\"primary\" (click)=\"createConnection()\">\n Add Your First Connection\n </button>\n </div>\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\" [innerHTML]=\"conn.Name | highlightSearch:(filters$ | async)?.searchTerm\"></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 kendoButton\n fillMode=\"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 kendoButton fillMode=\"flat\" (click)=\"editConnection(conn)\" title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button kendoButton fillMode=\"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 <div class=\"sync-result-banner\"\n [class.success]=\"getSyncState(conn.ID)?.lastResult?.Success\"\n [class.error]=\"!getSyncState(conn.ID)?.lastResult?.Success\">\n @if (getSyncState(conn.ID)?.lastResult?.Success) {\n <i class=\"fa-solid fa-check-circle\"></i>\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 <i class=\"fa-solid fa-exclamation-circle\"></i>\n <span>{{ getSyncState(conn.ID)?.lastResult?.ErrorMessage }}</span>\n }\n </div>\n }\n <div class=\"card-body\">\n @if (conn.Description) {\n <p class=\"description\" [innerHTML]=\"conn.Description | highlightSearch:(filters$ | async)?.searchTerm\"></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 kendoButton\n fillMode=\"flat\"\n size=\"small\"\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 (ServerGroups.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-wrench\"></i>\n <p>No tools discovered yet</p>\n <span class=\"hint\">Tools are discovered when connections sync with MCP servers</span>\n </div>\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\">{{ group.tools.length }} 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 kendoButton fillMode=\"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\" [innerHTML]=\"(tool.ToolTitle || tool.ToolName) | highlightSearch:(filters$ | async)?.searchTerm\"></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\" [innerHTML]=\"tool.ToolDescription | highlightSearch:(filters$ | async)?.searchTerm\"></p>\n }\n <div class=\"tool-card-actions\">\n <button kendoButton fillMode=\"flat\" size=\"small\" (click)=\"openTestToolDialog(tool)\" title=\"Test this tool\">\n <i class=\"fa-solid fa-play\"></i>\n Test\n </button>\n <button kendoButton fillMode=\"flat\" size=\"small\" (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\" [innerHTML]=\"(tool.ToolTitle || tool.ToolName) | highlightSearch:(filters$ | async)?.searchTerm\"></span>\n @if (tool.ToolTitle) {\n <span class=\"tool-code\">{{ tool.ToolName }}</span>\n }\n </div>\n </td>\n <td class=\"description-cell\" [innerHTML]=\"(tool.ToolDescription || '-') | highlightSearch:(filters$ | async)?.searchTerm\">\n </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 kendoButton fillMode=\"flat\" size=\"small\" (click)=\"openTestToolDialog(tool)\" title=\"Test\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n <button kendoButton fillMode=\"flat\" size=\"small\" (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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-list-check\"></i>\n <p>No recent execution logs</p>\n <span class=\"hint\">Logs appear when tools are executed via MCP connections</span>\n </div>\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 </div>\n </div>\n </div>\n</div>\n\n<!-- Server Dialog -->\n@if (ShowServerDialog) {\n <mj-mcp-server-dialog\n [server]=\"EditingServer\"\n [visible]=\"ShowServerDialog\"\n (close)=\"onServerDialogClose($event)\">\n </mj-mcp-server-dialog>\n}\n\n<!-- Connection Dialog -->\n@if (ShowConnectionDialog) {\n <mj-mcp-connection-dialog\n [connection]=\"EditingConnection\"\n [servers]=\"servers\"\n [visible]=\"ShowConnectionDialog\"\n (close)=\"onConnectionDialogClose($event)\">\n </mj-mcp-connection-dialog>\n}\n\n<!-- Test Tool Dialog -->\n@if (ShowTestToolDialog) {\n <mj-mcp-test-tool-dialog\n [Visible]=\"ShowTestToolDialog\"\n [Servers]=\"servers\"\n [Connections]=\"connections\"\n [Tools]=\"tools\"\n [SelectedServerID]=\"TestToolServerID\"\n [SelectedConnectionID]=\"TestToolConnectionID\"\n [SelectedToolID]=\"TestToolID\"\n (Close)=\"onTestToolDialogClose()\">\n </mj-mcp-test-tool-dialog>\n}\n\n<!-- Log Detail Panel -->\n@if (ShowLogDetailPanel && SelectedLog) {\n <mj-mcp-log-detail-panel\n [Visible]=\"ShowLogDetailPanel\"\n [Log]=\"SelectedLog\"\n (Close)=\"onLogDetailClose()\"\n (RunAgain)=\"onRunAgainFromLog($event)\">\n </mj-mcp-log-detail-panel>\n}\n", styles: ["/* MCP Dashboard - Header + Filter Panel Layout */\n.mcp-dashboard {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-page);\n overflow: hidden;\n}\n\n/* ========================================\n Dashboard Header\n ======================================== */\n.dashboard-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 20px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n gap: 16px;\n}\n\n.header-info {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.dashboard-title {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.dashboard-title i {\n color: var(--mj-brand-primary);\n font-size: 20px;\n}\n\n.filter-toggle-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-secondary);\n transition: all 0.2s ease;\n}\n\n.filter-toggle-btn:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n}\n\n.filter-toggle-btn i {\n font-size: 12px;\n}\n\n.item-count {\n font-size: 13px;\n color: var(--mj-text-secondary);\n padding: 4px 10px;\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n}\n\n.header-controls {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n/* Tab Navigation */\n.tab-nav {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-card);\n padding: 4px;\n border-radius: 8px;\n}\n\n.tab-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n background: transparent;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n transition: all 0.2s ease;\n}\n\n.tab-btn:hover {\n background: color-mix(in srgb, var(--mj-bg-surface) 70%, transparent);\n color: var(--mj-text-primary);\n}\n\n.tab-btn.active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n.tab-btn i {\n font-size: 14px;\n}\n\n.tab-label {\n font-weight: 500;\n}\n\n.tab-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 22px;\n height: 18px;\n padding: 0 6px;\n background: var(--mj-border-default);\n border-radius: 9px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-muted);\n}\n\n.tab-btn.active .tab-badge {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.tab-badge.has-errors {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n/* ========================================\n Main Content Area (with Filter Panel)\n ======================================== */\n.main-content {\n display: flex;\n flex-direction: row;\n flex: 1;\n overflow: hidden;\n min-width: 0;\n}\n\n/* Filter Panel Container */\n.filter-panel-container {\n display: flex;\n flex-shrink: 0;\n width: 280px;\n min-width: 200px;\n max-width: 400px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n position: relative;\n transition: width 0.2s ease, margin 0.2s ease;\n}\n\n.filter-panel-container.hidden {\n width: 0;\n min-width: 0;\n margin-left: -1px;\n overflow: hidden;\n}\n\n/* Resize Handle */\n.resize-handle {\n position: absolute;\n top: 0;\n right: 0;\n width: 4px;\n height: 100%;\n cursor: col-resize;\n background: transparent;\n transition: background 0.2s ease;\n z-index: 10;\n}\n\n.resize-handle:hover,\n.resize-handle:active {\n background: var(--mj-brand-primary);\n}\n\n/* Content Area */\n.content-area {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow: hidden;\n min-width: 0;\n}\n\n/* Content Body */\n.content-body {\n flex: 1;\n padding: 20px 24px;\n overflow: auto;\n}\n\n/* Error Banner */\n.error-banner {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n margin: 16px 24px 0 24px;\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border: 1px solid var(--mj-status-error);\n border-radius: 8px;\n color: var(--mj-status-error);\n}\n\n.error-banner .close-btn {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-status-error);\n padding: 4px;\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.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.sync-result-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n font-size: 12px;\n border-bottom: 1px solid;\n}\n\n.sync-result-banner.success {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-success) 20%, transparent);\n color: var(--mj-color-success-700);\n}\n\n.sync-result-banner.success i {\n color: var(--mj-status-success);\n}\n\n.sync-result-banner.error {\n background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-error) 20%, transparent);\n color: var(--mj-status-error);\n}\n\n.sync-result-banner.error i {\n color: var(--mj-status-error);\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/* Empty State */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n text-align: center;\n background: var(--mj-bg-surface);\n border-radius: 8px;\n border: 1px solid var(--mj-border-default);\n}\n\n.empty-state i {\n font-size: 48px;\n color: var(--mj-border-default);\n margin-bottom: 16px;\n}\n\n.empty-state p {\n margin: 0 0 8px 0;\n font-size: 16px;\n color: var(--mj-text-primary);\n}\n\n.empty-state .hint {\n font-size: 13px;\n color: var(--mj-text-secondary);\n margin-bottom: 16px;\n}\n\n/* ========================================\n View Mode Toggle\n ======================================== */\n.view-toggle {\n display: flex;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n}\n\n.view-toggle button {\n border-radius: 0;\n border: none;\n padding: 6px 10px;\n}\n\n.view-toggle button.active {\n background: var(--mj-brand-primary);\n color: var(--mj-color-neutral-0);\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"] }]
|
|
2763
2763
|
}], () => [{ type: i0.ChangeDetectorRef }, { type: i1.Router }, { type: i1.ActivatedRoute }, { type: i2.NavigationService }, { type: i3.MCPToolsService }], null); })();
|
|
2764
2764
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MCPDashboardComponent, { className: "MCPDashboardComponent", filePath: "src/MCP/mcp-dashboard.component.ts", lineNumber: 195 }); })();
|
|
2765
2765
|
//# sourceMappingURL=mcp-dashboard.component.js.map
|