@memberjunction/ng-dashboards 5.35.0 → 5.37.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.
Files changed (159) hide show
  1. package/dist/AI/components/agents/agent-configuration.component.js +3 -3
  2. package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
  3. package/dist/AI/components/analytics/ai-analytics-resource.component.d.ts +22 -1
  4. package/dist/AI/components/analytics/ai-analytics-resource.component.d.ts.map +1 -1
  5. package/dist/AI/components/analytics/ai-analytics-resource.component.js +157 -137
  6. package/dist/AI/components/analytics/ai-analytics-resource.component.js.map +1 -1
  7. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +28 -0
  8. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -1
  9. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +2075 -2068
  10. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
  11. package/dist/AI/components/models/model-management.component.js +4 -4
  12. package/dist/AI/components/models/model-management.component.js.map +1 -1
  13. package/dist/AI/components/prompts/prompt-management.component.js +3 -3
  14. package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
  15. package/dist/AI/components/tags/tags-resource.component.d.ts +15 -0
  16. package/dist/AI/components/tags/tags-resource.component.d.ts.map +1 -1
  17. package/dist/AI/components/tags/tags-resource.component.js +1411 -1424
  18. package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
  19. package/dist/APIKeys/api-keys-resource.component.d.ts +12 -8
  20. package/dist/APIKeys/api-keys-resource.component.d.ts.map +1 -1
  21. package/dist/APIKeys/api-keys-resource.component.js +329 -371
  22. package/dist/APIKeys/api-keys-resource.component.js.map +1 -1
  23. package/dist/Actions/components/actions-overview.component.js +137 -142
  24. package/dist/Actions/components/actions-overview.component.js.map +1 -1
  25. package/dist/Actions/components/execution-monitoring.component.js +111 -116
  26. package/dist/Actions/components/execution-monitoring.component.js.map +1 -1
  27. package/dist/Admin/admin-data-schema.component.js +13 -65
  28. package/dist/Admin/admin-data-schema.component.js.map +1 -1
  29. package/dist/Admin/admin-dev-tools-resource.component.js +13 -65
  30. package/dist/Admin/admin-dev-tools-resource.component.js.map +1 -1
  31. package/dist/Admin/admin-identity-access.component.js +13 -65
  32. package/dist/Admin/admin-identity-access.component.js.map +1 -1
  33. package/dist/Admin/admin-monitoring.component.js +13 -65
  34. package/dist/Admin/admin-monitoring.component.js.map +1 -1
  35. package/dist/Admin/base-admin-container.component.d.ts +9 -7
  36. package/dist/Admin/base-admin-container.component.d.ts.map +1 -1
  37. package/dist/Admin/base-admin-container.component.js +26 -17
  38. package/dist/Admin/base-admin-container.component.js.map +1 -1
  39. package/dist/ApplicationRoles/application-roles-resource.component.js +74 -67
  40. package/dist/ApplicationRoles/application-roles-resource.component.js.map +1 -1
  41. package/dist/Communication/communication-new-message-resource.component.d.ts +93 -0
  42. package/dist/Communication/communication-new-message-resource.component.d.ts.map +1 -0
  43. package/dist/Communication/communication-new-message-resource.component.js +661 -0
  44. package/dist/Communication/communication-new-message-resource.component.js.map +1 -0
  45. package/dist/Credentials/components/credentials-categories-resource.component.js +152 -159
  46. package/dist/Credentials/components/credentials-categories-resource.component.js.map +1 -1
  47. package/dist/Credentials/components/credentials-types-resource.component.js +151 -155
  48. package/dist/Credentials/components/credentials-types-resource.component.js.map +1 -1
  49. package/dist/DatabaseDesigner/components/database-designer-dashboard.component.js +20 -21
  50. package/dist/DatabaseDesigner/components/database-designer-dashboard.component.js.map +1 -1
  51. package/dist/DatabaseDesigner/components/entity-list.component.d.ts +2 -0
  52. package/dist/DatabaseDesigner/components/entity-list.component.d.ts.map +1 -1
  53. package/dist/DatabaseDesigner/components/entity-list.component.js +131 -125
  54. package/dist/DatabaseDesigner/components/entity-list.component.js.map +1 -1
  55. package/dist/DatabaseDesigner/database-designer-dashboards.module.d.ts +1 -1
  56. package/dist/DatabaseDesigner/database-designer-dashboards.module.d.ts.map +1 -1
  57. package/dist/DatabaseDesigner/database-designer-dashboards.module.js +7 -1
  58. package/dist/DatabaseDesigner/database-designer-dashboards.module.js.map +1 -1
  59. package/dist/DevTools/app-state-inspector.component.d.ts +5 -0
  60. package/dist/DevTools/app-state-inspector.component.d.ts.map +1 -1
  61. package/dist/DevTools/app-state-inspector.component.js +46 -72
  62. package/dist/DevTools/app-state-inspector.component.js.map +1 -1
  63. package/dist/DevTools/class-registry.component.js +88 -100
  64. package/dist/DevTools/class-registry.component.js.map +1 -1
  65. package/dist/DevTools/event-monitor.component.js +158 -168
  66. package/dist/DevTools/event-monitor.component.js.map +1 -1
  67. package/dist/DevTools/graphql-console.component.js +257 -264
  68. package/dist/DevTools/graphql-console.component.js.map +1 -1
  69. package/dist/DevTools/layout-inspector.component.d.ts +5 -0
  70. package/dist/DevTools/layout-inspector.component.d.ts.map +1 -1
  71. package/dist/DevTools/layout-inspector.component.js +46 -64
  72. package/dist/DevTools/layout-inspector.component.js.map +1 -1
  73. package/dist/DevTools/lazy-module-status.component.js +75 -84
  74. package/dist/DevTools/lazy-module-status.component.js.map +1 -1
  75. package/dist/DevTools/settings-explorer.component.js +76 -85
  76. package/dist/DevTools/settings-explorer.component.js.map +1 -1
  77. package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts +2 -0
  78. package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts.map +1 -1
  79. package/dist/EntityAdmin/entity-admin-dashboard.component.js +7 -3
  80. package/dist/EntityAdmin/entity-admin-dashboard.component.js.map +1 -1
  81. package/dist/Integration/components/activity/activity.component.js +97 -99
  82. package/dist/Integration/components/activity/activity.component.js.map +1 -1
  83. package/dist/Integration/components/connections/connections.component.js +842 -855
  84. package/dist/Integration/components/connections/connections.component.js.map +1 -1
  85. package/dist/Integration/components/pipelines/pipelines.component.js +502 -517
  86. package/dist/Integration/components/pipelines/pipelines.component.js.map +1 -1
  87. package/dist/Integration/components/schedules/schedules.component.js +78 -89
  88. package/dist/Integration/components/schedules/schedules.component.js.map +1 -1
  89. package/dist/KnowledgeHub/components/analytics/analytics-resource.component.d.ts +5 -0
  90. package/dist/KnowledgeHub/components/analytics/analytics-resource.component.d.ts.map +1 -1
  91. package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js +1120 -1128
  92. package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js.map +1 -1
  93. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.d.ts +11 -0
  94. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.d.ts.map +1 -1
  95. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +606 -661
  96. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
  97. package/dist/Lists/components/lists-browse-resource.component.d.ts +102 -0
  98. package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
  99. package/dist/Lists/components/lists-browse-resource.component.js +1179 -504
  100. package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
  101. package/dist/Lists/components/lists-operations-resource.component.d.ts +133 -3
  102. package/dist/Lists/components/lists-operations-resource.component.d.ts.map +1 -1
  103. package/dist/Lists/components/lists-operations-resource.component.js +1527 -327
  104. package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
  105. package/dist/Lists/components/lists-shared-with-me-resource.component.d.ts +29 -0
  106. package/dist/Lists/components/lists-shared-with-me-resource.component.d.ts.map +1 -0
  107. package/dist/Lists/components/lists-shared-with-me-resource.component.js +77 -0
  108. package/dist/Lists/components/lists-shared-with-me-resource.component.js.map +1 -0
  109. package/dist/Lists/components/venn-diagram/venn-diagram.component.d.ts +6 -0
  110. package/dist/Lists/components/venn-diagram/venn-diagram.component.d.ts.map +1 -1
  111. package/dist/Lists/components/venn-diagram/venn-diagram.component.js +35 -7
  112. package/dist/Lists/components/venn-diagram/venn-diagram.component.js.map +1 -1
  113. package/dist/Lists/index.d.ts +1 -0
  114. package/dist/Lists/index.d.ts.map +1 -1
  115. package/dist/Lists/index.js +1 -0
  116. package/dist/Lists/index.js.map +1 -1
  117. package/dist/Lists/services/list-set-operations.service.d.ts +93 -2
  118. package/dist/Lists/services/list-set-operations.service.d.ts.map +1 -1
  119. package/dist/Lists/services/list-set-operations.service.js +236 -10
  120. package/dist/Lists/services/list-set-operations.service.js.map +1 -1
  121. package/dist/MCP/mcp-dashboard.component.js +19 -19
  122. package/dist/MCP/mcp-dashboard.component.js.map +1 -1
  123. package/dist/Scheduling/scheduling-dashboard.component.js +58 -60
  124. package/dist/Scheduling/scheduling-dashboard.component.js.map +1 -1
  125. package/dist/SystemDiagnostics/system-diagnostics.component.d.ts +13 -3
  126. package/dist/SystemDiagnostics/system-diagnostics.component.d.ts.map +1 -1
  127. package/dist/SystemDiagnostics/system-diagnostics.component.js +1007 -1252
  128. package/dist/SystemDiagnostics/system-diagnostics.component.js.map +1 -1
  129. package/dist/Testing/components/testing-explorer.component.d.ts +31 -6
  130. package/dist/Testing/components/testing-explorer.component.d.ts.map +1 -1
  131. package/dist/Testing/components/testing-explorer.component.js +543 -629
  132. package/dist/Testing/components/testing-explorer.component.js.map +1 -1
  133. package/dist/Testing/testing-dashboard.component.js +50 -49
  134. package/dist/Testing/testing-dashboard.component.js.map +1 -1
  135. package/dist/ai-dashboards.module.d.ts +1 -1
  136. package/dist/ai-dashboards.module.d.ts.map +1 -1
  137. package/dist/ai-dashboards.module.js +16 -1
  138. package/dist/ai-dashboards.module.js.map +1 -1
  139. package/dist/communication-dashboards.module.d.ts +9 -7
  140. package/dist/communication-dashboards.module.d.ts.map +1 -1
  141. package/dist/communication-dashboards.module.js +13 -4
  142. package/dist/communication-dashboards.module.js.map +1 -1
  143. package/dist/core-dashboards.module.d.ts +1 -1
  144. package/dist/core-dashboards.module.d.ts.map +1 -1
  145. package/dist/core-dashboards.module.js +16 -1
  146. package/dist/core-dashboards.module.js.map +1 -1
  147. package/dist/lists-dashboards.module.d.ts +10 -9
  148. package/dist/lists-dashboards.module.d.ts.map +1 -1
  149. package/dist/lists-dashboards.module.js +13 -2
  150. package/dist/lists-dashboards.module.js.map +1 -1
  151. package/dist/public-api.d.ts +1 -0
  152. package/dist/public-api.d.ts.map +1 -1
  153. package/dist/public-api.js +1 -0
  154. package/dist/public-api.js.map +1 -1
  155. package/dist/testing-dashboards.module.d.ts +1 -1
  156. package/dist/testing-dashboards.module.d.ts.map +1 -1
  157. package/dist/testing-dashboards.module.js +13 -1
  158. package/dist/testing-dashboards.module.js.map +1 -1
  159. package/package.json +53 -52
@@ -33,34 +33,34 @@ const _forTrack0 = ($index, $item) => $item.ID;
33
33
  const _forTrack1 = ($index, $item) => $item.server.ID;
34
34
  const _forTrack2 = ($index, $item) => $item.value;
35
35
  const _forTrack3 = ($index, $item) => $item.name;
36
- function MCPDashboardComponent_Case_8_Template(rf, ctx) { if (rf & 1) {
36
+ function MCPDashboardComponent_Case_7_Template(rf, ctx) { if (rf & 1) {
37
37
  const _r1 = i0.ɵɵgetCurrentView();
38
38
  i0.ɵɵelementStart(0, "button", 34);
39
- i0.ɵɵlistener("click", function MCPDashboardComponent_Case_8_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.createServer()); });
39
+ i0.ɵɵlistener("click", function MCPDashboardComponent_Case_7_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.createServer()); });
40
40
  i0.ɵɵelement(1, "i", 35);
41
41
  i0.ɵɵtext(2, " Add Server ");
42
42
  i0.ɵɵelementEnd();
43
43
  } }
44
- function MCPDashboardComponent_Case_9_Template(rf, ctx) { if (rf & 1) {
44
+ function MCPDashboardComponent_Case_8_Template(rf, ctx) { if (rf & 1) {
45
45
  const _r3 = i0.ɵɵgetCurrentView();
46
46
  i0.ɵɵelementStart(0, "button", 34);
47
- i0.ɵɵlistener("click", function MCPDashboardComponent_Case_9_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.createConnection()); });
47
+ i0.ɵɵlistener("click", function MCPDashboardComponent_Case_8_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.createConnection()); });
48
48
  i0.ɵɵelement(1, "i", 35);
49
49
  i0.ɵɵtext(2, " Add Connection ");
50
50
  i0.ɵɵelementEnd();
51
51
  } }
52
- function MCPDashboardComponent_Case_10_Template(rf, ctx) { if (rf & 1) {
52
+ function MCPDashboardComponent_Case_9_Template(rf, ctx) { if (rf & 1) {
53
53
  const _r4 = i0.ɵɵgetCurrentView();
54
54
  i0.ɵɵelementStart(0, "mj-view-toggle", 36);
55
- i0.ɵɵlistener("KeyChange", function MCPDashboardComponent_Case_10_Template_mj_view_toggle_KeyChange_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setToolsViewMode($event)); });
55
+ i0.ɵɵlistener("KeyChange", function MCPDashboardComponent_Case_9_Template_mj_view_toggle_KeyChange_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setToolsViewMode($event)); });
56
56
  i0.ɵɵelementEnd();
57
57
  i0.ɵɵelementStart(1, "button", 37);
58
- i0.ɵɵlistener("click", function MCPDashboardComponent_Case_10_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.toggleScalableMode(!ctx_r1.useScalablePagination)); });
58
+ i0.ɵɵlistener("click", function MCPDashboardComponent_Case_9_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.toggleScalableMode(!ctx_r1.useScalablePagination)); });
59
59
  i0.ɵɵelement(2, "i", 38);
60
60
  i0.ɵɵtext(3);
61
61
  i0.ɵɵelementEnd();
62
62
  i0.ɵɵelementStart(4, "button", 34);
63
- i0.ɵɵlistener("click", function MCPDashboardComponent_Case_10_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.openTestToolDialog()); });
63
+ i0.ɵɵlistener("click", function MCPDashboardComponent_Case_9_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.openTestToolDialog()); });
64
64
  i0.ɵɵelement(5, "i", 39);
65
65
  i0.ɵɵtext(6, " Test Tool ");
66
66
  i0.ɵɵelementEnd();
@@ -74,10 +74,10 @@ function MCPDashboardComponent_Case_10_Template(rf, ctx) { if (rf & 1) {
74
74
  i0.ɵɵadvance();
75
75
  i0.ɵɵtextInterpolate1(" Scale: ", ctx_r1.useScalablePagination ? "ON" : "OFF", " ");
76
76
  } }
77
- function MCPDashboardComponent_Case_11_Template(rf, ctx) { if (rf & 1) {
77
+ function MCPDashboardComponent_Case_10_Template(rf, ctx) { if (rf & 1) {
78
78
  const _r5 = i0.ɵɵgetCurrentView();
79
79
  i0.ɵɵelementStart(0, "mj-refresh-button", 40);
80
- i0.ɵɵlistener("Clicked", function MCPDashboardComponent_Case_11_Template_mj_refresh_button_Clicked_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.loadAllData()); });
80
+ i0.ɵɵlistener("Clicked", function MCPDashboardComponent_Case_10_Template_mj_refresh_button_Clicked_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.loadAllData()); });
81
81
  i0.ɵɵelementEnd();
82
82
  } if (rf & 2) {
83
83
  const ctx_r1 = i0.ɵɵnextContext();
@@ -4290,7 +4290,7 @@ let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
4290
4290
  this.cdr.detectChanges();
4291
4291
  }
4292
4292
  static ɵfac = function MCPDashboardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MCPDashboardComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.MCPToolsService)); };
4293
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MCPDashboardComponent, selectors: [["mj-mcp-dashboard"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 22, vars: 17, consts: [["sfName", ""], ["sfDesc", ""], ["sfTransport", ""], ["sfAuth", ""], ["sfStatus", ""], ["sfUrl", ""], ["sfCmd", ""], ["cfServer", ""], ["cfName", ""], ["cfToken", ""], ["cfStatus", ""], ["tSrv", ""], ["tConn", ""], ["tSearch", ""], ["enumEl", ""], ["boolEl", ""], ["jsonEl", ""], ["strEl", ""], ["Title", "MCP Dashboard", "Icon", "fa-solid fa-plug-circle-bolt"], ["meta", ""], ["Label", "items", 3, "Count", "Total"], ["actions", ""], [3, "ClearAllRequested", "ActiveCount", "ShowClearAll"], [3, "ValuesChange", "Reset", "Fields", "Values"], [3, "TabChange", "Tabs", "ActiveKey"], ["mjButton", "", "variant", "primary", "size", "sm"], [3, "Loading"], ["toolbar", ""], ["Placeholder", "Search\u2026", 3, "ValueChange", "Value"], [3, "Flex"], [1, "error-banner"], ["text", "Loading MCP data..."], [2, "position", "fixed", "inset", "0", "z-index", "10000", "background", "rgba(0,0,0,0.5)", "display", "flex", "align-items", "center", "justify-content", "center"], [2, "position", "fixed", "inset", "0", "z-index", "10000", "pointer-events", "none"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-plus"], [3, "KeyChange", "Options", "ActiveKey"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click", "title"], [1, "fa-solid"], [1, "fa-solid", "fa-play"], [3, "Clicked", "Loading"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "data-grid", "servers-grid"], [1, "data-grid", "connections-grid"], [1, "data-table"], [1, "empty-state"], [1, "fa-solid", "fa-server"], [1, "data-card", 3, "expanded"], [1, "data-card"], [1, "card-header", "clickable", 3, "click"], [1, "card-title"], [1, "fa-solid", "fa-chevron-right", "expand-arrow"], [1, "name"], [1, "status-badge", 3, "ngClass"], [1, "card-actions", 3, "click"], ["mjButton", "", "variant", "flat", "title", "Edit", 3, "click"], [1, "fa-solid", "fa-pen"], ["mjButton", "", "variant", "flat", "title", "Delete", 3, "click"], [1, "fa-solid", "fa-trash"], [1, "card-body"], [1, "description"], [1, "details-grid"], [1, "detail"], [1, "label"], [1, "value"], [1, "detail", "full-width"], [1, "expanded-tools-section"], [1, "value", "url"], [1, "value", "command"], [1, "tools-section-header"], [1, "fa-solid", "fa-wrench"], [1, "no-tools-message"], [1, "tools-mini-list"], [1, "fa-solid", "fa-info-circle"], [1, "tool-mini-card"], [1, "tool-mini-info"], [1, "tool-mini-name"], [1, "tool-mini-params"], [1, "fa-solid", "fa-sliders"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Test this tool", 3, "click"], [1, "fa-solid", "fa-link"], ["mjButton", "", "variant", "flat", "title", "Sync Tools", 3, "click", "disabled"], [1, "fa-solid", "fa-sync", "fa-spin"], [1, "fa-solid", "fa-sync"], [1, "sync-progress-banner"], [1, "sync-result-banner", 3, "success", "error"], [1, "detail", "full-width", "error"], [1, "fa-solid", "fa-circle-notch", "fa-spin"], [1, "sync-message"], [1, "sync-result-banner"], [1, "fa-solid", "fa-check-circle"], [1, "fa-solid", "fa-exclamation-circle"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Test this tool with this connection", 3, "click"], [1, "tools-container"], [2, "padding", "8px 12px", "font-size", "0.8rem", "color", "var(--mj-text-secondary,#475569)", "background", "var(--mj-bg-surface-card,#f8f9fa)", "border-radius", "4px", "margin-bottom", "6px"], [2, "height", "calc(100vh - 260px)", "width", "100%", "overflow-y", "auto", "border", "1px solid var(--mj-border-subtle,#eee)", "border-radius", "4px", 3, "scroll"], [2, "display", "flex", "align-items", "center", "gap", "12px", "padding", "12px 14px", "border-bottom", "1px solid var(--mj-border-subtle,#eee)", "height", "72px", "box-sizing", "border-box"], [2, "padding", "12px", "text-align", "center", "font-size", "0.8rem", "color", "var(--mj-text-muted,#888)"], [1, "fa-solid", "fa-wrench", 2, "color", "var(--mj-brand-primary,#264FAF)"], [2, "flex", "1", "min-width", "0"], [2, "font-weight", "500", "font-size", "0.875rem", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], ["type", "button", 2, "background", "transparent", "border", "none", "cursor", "pointer", "padding", "4px", "font-size", "1rem", 3, "click", "title"], [1, "status-badge", "small", 3, "ngClass"], [1, "hint"], [1, "server-group", 3, "collapsed"], [1, "server-group"], [1, "server-group-header", 3, "click"], [1, "server-info"], [1, "fa-solid", "fa-chevron-right", "expand-icon"], [1, "server-name"], [1, "tool-count"], [1, "server-actions", 3, "click"], ["mjButton", "", "variant", "flat", "title", "Test a tool", 3, "click"], [1, "tools-grid"], [1, "tools-list"], [1, "tool-card", 3, "expanded"], [1, "tool-card"], [1, "tool-card-header", 3, "click"], [1, "tool-title"], [1, "tool-meta"], ["title", "Parameters", 1, "param-badge"], [1, "tool-description"], [1, "tool-card-actions"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "View details", 3, "click"], [1, "tool-details"], [1, "detail-row"], [1, "detail-label"], [1, "detail-value", "mono"], [1, "detail-value"], [1, "detail-row", "full"], [1, "schema-preview"], [1, "tool-name-cell"], [1, "tool-name-info"], [1, "tool-code"], [1, "description-cell"], [1, "param-count"], [1, "actions-cell"], ["type", "button", 2, "background", "transparent", "border", "none", "cursor", "pointer", "padding", "4px", "font-size", "0.95rem", "margin-right", "4px", 3, "click", "title"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Test", 3, "click"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Details", 3, "click"], [1, "detail-row-expanded"], ["colspan", "6"], [1, "inline-details"], [1, "detail-section"], [1, "detail-section", "full"], [1, "fa-solid", "fa-list-check"], [1, "sortable", 3, "click", "ngClass"], [1, "sort-icon", "fa-solid"], ["title", "Click for details", 1, "clickable-row", 3, "error-row"], ["title", "Click for details", 1, "clickable-row", 3, "click"], [1, "fa-solid", "fa-times-circle"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-question-circle"], [1, "tool-name"], [1, "error-message", 3, "title"], [1, "action-cell"], [1, "fa-solid", "fa-chevron-right"], [2, "position", "fixed", "inset", "0", "z-index", "10000", "background", "rgba(0,0,0,0.5)", "display", "flex", "align-items", "center", "justify-content", "center", 3, "click"], [2, "display", "flex", "flex-direction", "column", "background", "var(--mj-bg-surface,#fff)", "border-radius", "8px", "box-shadow", "0 20px 60px rgba(0,0,0,0.3)", "overflow", "hidden", "width", "600px", "max-width", "90vw", "max-height", "90vh", 3, "click"], [2, "display", "flex", "align-items", "center", "justify-content", "space-between", "padding", "16px 20px", "border-bottom", "1px solid var(--mj-border-default,#e0e0e0)", "flex-shrink", "0"], [2, "margin", "0", "font-size", "1.125rem", "font-weight", "600", "color", "var(--mj-text-primary,#333)"], [2, "display", "flex", "align-items", "center", "justify-content", "center", "width", "32px", "height", "32px", "border", "none", "background", "none", "cursor", "pointer", "color", "var(--mj-text-muted,#888)", "border-radius", "4px", 3, "click"], [2, "flex", "1", "overflow-y", "auto", "padding", "20px", "color", "var(--mj-text-primary,#333)"], [2, "background", "var(--mj-status-error-bg,#fff5f5)", "border", "1px solid var(--mj-status-error-border,#feb2b2)", "border-radius", "6px", "padding", "10px 14px", "margin-bottom", "16px", "color", "var(--mj-status-error-text,#c53030)", "font-size", "0.875rem"], [2, "margin-bottom", "16px"], [2, "display", "block", "font-size", "0.875rem", "font-weight", "500", "color", "var(--mj-text-primary,#333)", "margin-bottom", "4px"], [2, "color", "var(--mj-status-error,#e53e3e)"], ["placeholder", "e.g., GitHub MCP Server", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], ["placeholder", "Optional description", "rows", "3", 1, "mj-textarea", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "change", "value"], [3, "value"], ["value", "Active"], ["value", "Inactive"], [2, "display", "flex", "align-items", "center", "gap", "8px", "padding", "16px 20px", "border-top", "1px solid var(--mj-border-default,#e0e0e0)", "flex-shrink", "0"], ["mjButton", "", "variant", "primary", 3, "click", "disabled"], ["mjButton", "", 3, "click"], ["placeholder", "https://api.example.com/mcp", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], ["placeholder", "e.g., npx", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], [2, "display", "flex", "flex-direction", "column", "background", "var(--mj-bg-surface,#fff)", "border-radius", "8px", "box-shadow", "0 20px 60px rgba(0,0,0,0.3)", "overflow", "hidden", "width", "560px", "max-width", "90vw", "max-height", "90vh", 3, "click"], ["value", ""], ["placeholder", "e.g., Zapier Connection", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], ["placeholder", "Paste bearer token here", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", "font-family", "monospace", "font-size", "0.8rem", 3, "input", "value"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)"], [2, "display", "flex", "flex-direction", "column", "background", "var(--mj-bg-surface,#fff)", "border-radius", "8px", "box-shadow", "0 20px 60px rgba(0,0,0,0.3)", "overflow", "hidden", "width", "640px", "max-width", "90vw", "max-height", "90vh", 3, "click"], [2, "font-size", "0.875rem", "font-weight", "400", "color", "var(--mj-text-muted,#888)", "margin-left", "8px"], [2, "display", "flex", "gap", "0", "padding", "10px 20px", "background", "var(--mj-bg-surface-card,#f9f9f9)", "border-bottom", "1px solid var(--mj-border-default,#e0e0e0)", "flex-shrink", "0"], [2, "font-size", "0.8rem", "font-weight", "500"], [2, "margin", "0 8px", "color", "var(--mj-text-muted,#888)"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "change"], [3, "value", "selected"], ["type", "text", "placeholder", "Search tools\u2026 (Recently used appear first)", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", "margin-bottom", "6px", 3, "input", "value"], [2, "max-height", "220px", "overflow-y", "auto", "border", "1px solid var(--mj-border-default,#e0e0e0)", "border-radius", "4px", "background", "var(--mj-bg-surface,#fff)"], [2, "padding", "8px 12px", "cursor", "pointer", "display", "flex", "align-items", "center", "gap", "8px", "border-bottom", "1px solid var(--mj-border-subtle,#eee)", 3, "background"], [2, "padding", "8px 12px", "cursor", "pointer", "display", "flex", "align-items", "center", "gap", "8px", "border-bottom", "1px solid var(--mj-border-subtle,#eee)", 3, "click"], [2, "font-size", "0.65rem", "background", "var(--mj-brand-primary,#264FAF)", "color", "#fff", "padding", "1px 6px", "border-radius", "8px", "font-weight", "600"], [2, "font-size", "0.85rem", "font-weight", "500", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], [2, "font-size", "0.7rem", "color", "var(--mj-text-muted,#888)", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], [1, "fa-solid", "fa-check", 2, "color", "var(--mj-brand-primary,#264FAF)"], [2, "text-align", "center", "padding", "24px", "color", "var(--mj-text-muted,#888)"], [1, "fa-solid", "fa-check-circle", 2, "font-size", "2rem", "margin-bottom", "8px", "display", "block"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin", "0 0 4px 0"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box"], ["rows", "3", 1, "mj-textarea", 2, "width", "100%", "box-sizing", "border-box", "font-family", "monospace", "font-size", "0.8rem", 3, "placeholder"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "placeholder"], ["value", "false"], ["value", "true"], ["rows", "3", 1, "mj-textarea", 2, "width", "100%", "box-sizing", "border-box", "font-family", "monospace", "font-size", "0.8rem", 3, "input", "placeholder"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "placeholder"], [2, "background", "var(--mj-status-success-bg,#f0fff4)", "border", "1px solid var(--mj-status-success-border,#9ae6b4)", "border-radius", "6px", "padding", "12px 16px", "margin-bottom", "16px", "color", "var(--mj-status-success-text,#276749)"], [2, "background", "var(--mj-status-error-bg,#fff5f5)", "border", "1px solid var(--mj-status-error-border,#feb2b2)", "border-radius", "6px", "padding", "12px 16px", "margin-bottom", "16px", "color", "var(--mj-status-error-text,#c53030)"], [2, "float", "right", "font-size", "0.75rem"], [2, "display", "block", "font-size", "0.875rem", "font-weight", "500", "margin-bottom", "4px"], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.8rem", "white-space", "pre-wrap", "word-break", "break-all", "margin", "0"], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.8rem", "white-space", "pre-wrap", "word-break", "break-all", "overflow-y", "auto", "max-height", "300px", "margin", "0"], [1, "fa-solid", "fa-arrow-right"], [1, "fa-solid", "fa-arrow-left"], ["mjButton", "", "variant", "primary", 3, "click"], [1, "fa-solid", "fa-redo"], [2, "position", "absolute", "inset", "0", "background", "rgba(0,0,0,0.35)", "pointer-events", "auto", 3, "click"], [2, "position", "absolute", "top", "0", "right", "0", "height", "100%", "width", "560px", "max-width", "95vw", "background", "var(--mj-bg-surface,#fff)", "box-shadow", "-4px 0 20px rgba(0,0,0,0.15)", "display", "flex", "flex-direction", "column", "pointer-events", "auto"], [2, "padding", "16px 20px", "border-bottom", "1px solid var(--mj-border-default,#e0e0e0)", "display", "flex", "align-items", "center", "justify-content", "space-between"], [2, "margin", "0", "font-size", "1rem", "font-weight", "600"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin-top", "4px"], [2, "font-weight", "600"], [2, "margin", "0 8px"], ["mjButton", "", "aria-label", "Close", 3, "click"], [1, "fa-solid", "fa-xmark"], [2, "flex", "1", "overflow-y", "auto", "padding", "16px 20px"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin-bottom", "4px"], [2, "font-size", "0.875rem"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin-top", "2px"], [2, "padding", "12px 20px", "border-top", "1px solid var(--mj-border-default,#e0e0e0)", "display", "flex", "gap", "8px", "justify-content", "flex-start"], ["mjButton", "", "variant", "primary"], [2, "display", "block", "font-size", "0.875rem", "font-weight", "500", "margin-bottom", "6px"], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.75rem", "white-space", "pre-wrap", "word-break", "break-all", "overflow-y", "auto", "max-height", "240px", "margin", "0"], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.75rem", "white-space", "pre-wrap", "word-break", "break-all", "overflow-y", "auto", "max-height", "360px", "margin", "0"], [2, "display", "block", "font-size", "0.875rem", "font-weight", "500", "margin-bottom", "6px", "color", "var(--mj-status-error-text,#c53030)"], [2, "background", "var(--mj-status-error-bg,#fff5f5)", "border", "1px solid var(--mj-status-error-border,#feb2b2)", "border-radius", "4px", "padding", "12px", "font-size", "0.75rem", "white-space", "pre-wrap", "word-break", "break-all", "margin", "0", "color", "var(--mj-status-error-text,#c53030)"]], template: function MCPDashboardComponent_Template(rf, ctx) { if (rf & 1) {
4293
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MCPDashboardComponent, selectors: [["mj-mcp-dashboard"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 22, vars: 17, consts: [["sfName", ""], ["sfDesc", ""], ["sfTransport", ""], ["sfAuth", ""], ["sfStatus", ""], ["sfUrl", ""], ["sfCmd", ""], ["cfServer", ""], ["cfName", ""], ["cfToken", ""], ["cfStatus", ""], ["tSrv", ""], ["tConn", ""], ["tSearch", ""], ["enumEl", ""], ["boolEl", ""], ["jsonEl", ""], ["strEl", ""], ["Title", "MCP Dashboard", "Icon", "fa-solid fa-plug-circle-bolt"], ["meta", ""], ["Label", "items", 3, "Count", "Total"], ["actions", ""], [3, "ClearAllRequested", "ActiveCount", "ShowClearAll"], [3, "ValuesChange", "Reset", "Fields", "Values"], ["mjButton", "", "variant", "primary", "size", "sm"], [3, "Loading"], ["toolbar", ""], [3, "TabChange", "Tabs", "ActiveKey"], ["Placeholder", "Search\u2026", 3, "ValueChange", "Value"], [3, "Flex"], [1, "error-banner"], ["text", "Loading MCP data..."], [2, "position", "fixed", "inset", "0", "z-index", "10000", "background", "rgba(0,0,0,0.5)", "display", "flex", "align-items", "center", "justify-content", "center"], [2, "position", "fixed", "inset", "0", "z-index", "10000", "pointer-events", "none"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-plus"], [3, "KeyChange", "Options", "ActiveKey"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click", "title"], [1, "fa-solid"], [1, "fa-solid", "fa-play"], [3, "Clicked", "Loading"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "data-grid", "servers-grid"], [1, "data-grid", "connections-grid"], [1, "data-table"], [1, "empty-state"], [1, "fa-solid", "fa-server"], [1, "data-card", 3, "expanded"], [1, "data-card"], [1, "card-header", "clickable", 3, "click"], [1, "card-title"], [1, "fa-solid", "fa-chevron-right", "expand-arrow"], [1, "name"], [1, "status-badge", 3, "ngClass"], [1, "card-actions", 3, "click"], ["mjButton", "", "variant", "flat", "title", "Edit", 3, "click"], [1, "fa-solid", "fa-pen"], ["mjButton", "", "variant", "flat", "title", "Delete", 3, "click"], [1, "fa-solid", "fa-trash"], [1, "card-body"], [1, "description"], [1, "details-grid"], [1, "detail"], [1, "label"], [1, "value"], [1, "detail", "full-width"], [1, "expanded-tools-section"], [1, "value", "url"], [1, "value", "command"], [1, "tools-section-header"], [1, "fa-solid", "fa-wrench"], [1, "no-tools-message"], [1, "tools-mini-list"], [1, "fa-solid", "fa-info-circle"], [1, "tool-mini-card"], [1, "tool-mini-info"], [1, "tool-mini-name"], [1, "tool-mini-params"], [1, "fa-solid", "fa-sliders"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Test this tool", 3, "click"], [1, "fa-solid", "fa-link"], ["mjButton", "", "variant", "flat", "title", "Sync Tools", 3, "click", "disabled"], [1, "fa-solid", "fa-sync", "fa-spin"], [1, "fa-solid", "fa-sync"], [1, "sync-progress-banner"], [1, "sync-result-banner", 3, "success", "error"], [1, "detail", "full-width", "error"], [1, "fa-solid", "fa-circle-notch", "fa-spin"], [1, "sync-message"], [1, "sync-result-banner"], [1, "fa-solid", "fa-check-circle"], [1, "fa-solid", "fa-exclamation-circle"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Test this tool with this connection", 3, "click"], [1, "tools-container"], [2, "padding", "8px 12px", "font-size", "0.8rem", "color", "var(--mj-text-secondary,#475569)", "background", "var(--mj-bg-surface-card,#f8f9fa)", "border-radius", "4px", "margin-bottom", "6px"], [2, "height", "calc(100vh - 260px)", "width", "100%", "overflow-y", "auto", "border", "1px solid var(--mj-border-subtle,#eee)", "border-radius", "4px", 3, "scroll"], [2, "display", "flex", "align-items", "center", "gap", "12px", "padding", "12px 14px", "border-bottom", "1px solid var(--mj-border-subtle,#eee)", "height", "72px", "box-sizing", "border-box"], [2, "padding", "12px", "text-align", "center", "font-size", "0.8rem", "color", "var(--mj-text-muted,#888)"], [1, "fa-solid", "fa-wrench", 2, "color", "var(--mj-brand-primary,#264FAF)"], [2, "flex", "1", "min-width", "0"], [2, "font-weight", "500", "font-size", "0.875rem", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], ["type", "button", 2, "background", "transparent", "border", "none", "cursor", "pointer", "padding", "4px", "font-size", "1rem", 3, "click", "title"], [1, "status-badge", "small", 3, "ngClass"], [1, "hint"], [1, "server-group", 3, "collapsed"], [1, "server-group"], [1, "server-group-header", 3, "click"], [1, "server-info"], [1, "fa-solid", "fa-chevron-right", "expand-icon"], [1, "server-name"], [1, "tool-count"], [1, "server-actions", 3, "click"], ["mjButton", "", "variant", "flat", "title", "Test a tool", 3, "click"], [1, "tools-grid"], [1, "tools-list"], [1, "tool-card", 3, "expanded"], [1, "tool-card"], [1, "tool-card-header", 3, "click"], [1, "tool-title"], [1, "tool-meta"], ["title", "Parameters", 1, "param-badge"], [1, "tool-description"], [1, "tool-card-actions"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "View details", 3, "click"], [1, "tool-details"], [1, "detail-row"], [1, "detail-label"], [1, "detail-value", "mono"], [1, "detail-value"], [1, "detail-row", "full"], [1, "schema-preview"], [1, "tool-name-cell"], [1, "tool-name-info"], [1, "tool-code"], [1, "description-cell"], [1, "param-count"], [1, "actions-cell"], ["type", "button", 2, "background", "transparent", "border", "none", "cursor", "pointer", "padding", "4px", "font-size", "0.95rem", "margin-right", "4px", 3, "click", "title"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Test", 3, "click"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Details", 3, "click"], [1, "detail-row-expanded"], ["colspan", "6"], [1, "inline-details"], [1, "detail-section"], [1, "detail-section", "full"], [1, "fa-solid", "fa-list-check"], [1, "sortable", 3, "click", "ngClass"], [1, "sort-icon", "fa-solid"], ["title", "Click for details", 1, "clickable-row", 3, "error-row"], ["title", "Click for details", 1, "clickable-row", 3, "click"], [1, "fa-solid", "fa-times-circle"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-question-circle"], [1, "tool-name"], [1, "error-message", 3, "title"], [1, "action-cell"], [1, "fa-solid", "fa-chevron-right"], [2, "position", "fixed", "inset", "0", "z-index", "10000", "background", "rgba(0,0,0,0.5)", "display", "flex", "align-items", "center", "justify-content", "center", 3, "click"], [2, "display", "flex", "flex-direction", "column", "background", "var(--mj-bg-surface,#fff)", "border-radius", "8px", "box-shadow", "0 20px 60px rgba(0,0,0,0.3)", "overflow", "hidden", "width", "600px", "max-width", "90vw", "max-height", "90vh", 3, "click"], [2, "display", "flex", "align-items", "center", "justify-content", "space-between", "padding", "16px 20px", "border-bottom", "1px solid var(--mj-border-default,#e0e0e0)", "flex-shrink", "0"], [2, "margin", "0", "font-size", "1.125rem", "font-weight", "600", "color", "var(--mj-text-primary,#333)"], [2, "display", "flex", "align-items", "center", "justify-content", "center", "width", "32px", "height", "32px", "border", "none", "background", "none", "cursor", "pointer", "color", "var(--mj-text-muted,#888)", "border-radius", "4px", 3, "click"], [2, "flex", "1", "overflow-y", "auto", "padding", "20px", "color", "var(--mj-text-primary,#333)"], [2, "background", "var(--mj-status-error-bg,#fff5f5)", "border", "1px solid var(--mj-status-error-border,#feb2b2)", "border-radius", "6px", "padding", "10px 14px", "margin-bottom", "16px", "color", "var(--mj-status-error-text,#c53030)", "font-size", "0.875rem"], [2, "margin-bottom", "16px"], [2, "display", "block", "font-size", "0.875rem", "font-weight", "500", "color", "var(--mj-text-primary,#333)", "margin-bottom", "4px"], [2, "color", "var(--mj-status-error,#e53e3e)"], ["placeholder", "e.g., GitHub MCP Server", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], ["placeholder", "Optional description", "rows", "3", 1, "mj-textarea", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "change", "value"], [3, "value"], ["value", "Active"], ["value", "Inactive"], [2, "display", "flex", "align-items", "center", "gap", "8px", "padding", "16px 20px", "border-top", "1px solid var(--mj-border-default,#e0e0e0)", "flex-shrink", "0"], ["mjButton", "", "variant", "primary", 3, "click", "disabled"], ["mjButton", "", 3, "click"], ["placeholder", "https://api.example.com/mcp", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], ["placeholder", "e.g., npx", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], [2, "display", "flex", "flex-direction", "column", "background", "var(--mj-bg-surface,#fff)", "border-radius", "8px", "box-shadow", "0 20px 60px rgba(0,0,0,0.3)", "overflow", "hidden", "width", "560px", "max-width", "90vw", "max-height", "90vh", 3, "click"], ["value", ""], ["placeholder", "e.g., Zapier Connection", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "value"], ["placeholder", "Paste bearer token here", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", "font-family", "monospace", "font-size", "0.8rem", 3, "input", "value"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)"], [2, "display", "flex", "flex-direction", "column", "background", "var(--mj-bg-surface,#fff)", "border-radius", "8px", "box-shadow", "0 20px 60px rgba(0,0,0,0.3)", "overflow", "hidden", "width", "640px", "max-width", "90vw", "max-height", "90vh", 3, "click"], [2, "font-size", "0.875rem", "font-weight", "400", "color", "var(--mj-text-muted,#888)", "margin-left", "8px"], [2, "display", "flex", "gap", "0", "padding", "10px 20px", "background", "var(--mj-bg-surface-card,#f9f9f9)", "border-bottom", "1px solid var(--mj-border-default,#e0e0e0)", "flex-shrink", "0"], [2, "font-size", "0.8rem", "font-weight", "500"], [2, "margin", "0 8px", "color", "var(--mj-text-muted,#888)"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "change"], [3, "value", "selected"], ["type", "text", "placeholder", "Search tools\u2026 (Recently used appear first)", 1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", "margin-bottom", "6px", 3, "input", "value"], [2, "max-height", "220px", "overflow-y", "auto", "border", "1px solid var(--mj-border-default,#e0e0e0)", "border-radius", "4px", "background", "var(--mj-bg-surface,#fff)"], [2, "padding", "8px 12px", "cursor", "pointer", "display", "flex", "align-items", "center", "gap", "8px", "border-bottom", "1px solid var(--mj-border-subtle,#eee)", 3, "background"], [2, "padding", "8px 12px", "cursor", "pointer", "display", "flex", "align-items", "center", "gap", "8px", "border-bottom", "1px solid var(--mj-border-subtle,#eee)", 3, "click"], [2, "font-size", "0.65rem", "background", "var(--mj-brand-primary,#264FAF)", "color", "#fff", "padding", "1px 6px", "border-radius", "8px", "font-weight", "600"], [2, "font-size", "0.85rem", "font-weight", "500", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], [2, "font-size", "0.7rem", "color", "var(--mj-text-muted,#888)", "overflow", "hidden", "text-overflow", "ellipsis", "white-space", "nowrap"], [1, "fa-solid", "fa-check", 2, "color", "var(--mj-brand-primary,#264FAF)"], [2, "text-align", "center", "padding", "24px", "color", "var(--mj-text-muted,#888)"], [1, "fa-solid", "fa-check-circle", 2, "font-size", "2rem", "margin-bottom", "8px", "display", "block"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin", "0 0 4px 0"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box"], ["rows", "3", 1, "mj-textarea", 2, "width", "100%", "box-sizing", "border-box", "font-family", "monospace", "font-size", "0.8rem", 3, "placeholder"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "placeholder"], ["value", "false"], ["value", "true"], ["rows", "3", 1, "mj-textarea", 2, "width", "100%", "box-sizing", "border-box", "font-family", "monospace", "font-size", "0.8rem", 3, "input", "placeholder"], [1, "mj-input", 2, "width", "100%", "box-sizing", "border-box", 3, "input", "placeholder"], [2, "background", "var(--mj-status-success-bg,#f0fff4)", "border", "1px solid var(--mj-status-success-border,#9ae6b4)", "border-radius", "6px", "padding", "12px 16px", "margin-bottom", "16px", "color", "var(--mj-status-success-text,#276749)"], [2, "background", "var(--mj-status-error-bg,#fff5f5)", "border", "1px solid var(--mj-status-error-border,#feb2b2)", "border-radius", "6px", "padding", "12px 16px", "margin-bottom", "16px", "color", "var(--mj-status-error-text,#c53030)"], [2, "float", "right", "font-size", "0.75rem"], [2, "display", "block", "font-size", "0.875rem", "font-weight", "500", "margin-bottom", "4px"], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.8rem", "white-space", "pre-wrap", "word-break", "break-all", "margin", "0"], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.8rem", "white-space", "pre-wrap", "word-break", "break-all", "overflow-y", "auto", "max-height", "300px", "margin", "0"], [1, "fa-solid", "fa-arrow-right"], [1, "fa-solid", "fa-arrow-left"], ["mjButton", "", "variant", "primary", 3, "click"], [1, "fa-solid", "fa-redo"], [2, "position", "absolute", "inset", "0", "background", "rgba(0,0,0,0.35)", "pointer-events", "auto", 3, "click"], [2, "position", "absolute", "top", "0", "right", "0", "height", "100%", "width", "560px", "max-width", "95vw", "background", "var(--mj-bg-surface,#fff)", "box-shadow", "-4px 0 20px rgba(0,0,0,0.15)", "display", "flex", "flex-direction", "column", "pointer-events", "auto"], [2, "padding", "16px 20px", "border-bottom", "1px solid var(--mj-border-default,#e0e0e0)", "display", "flex", "align-items", "center", "justify-content", "space-between"], [2, "margin", "0", "font-size", "1rem", "font-weight", "600"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin-top", "4px"], [2, "font-weight", "600"], [2, "margin", "0 8px"], ["mjButton", "", "aria-label", "Close", 3, "click"], [1, "fa-solid", "fa-xmark"], [2, "flex", "1", "overflow-y", "auto", "padding", "16px 20px"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin-bottom", "4px"], [2, "font-size", "0.875rem"], [2, "font-size", "0.75rem", "color", "var(--mj-text-muted,#888)", "margin-top", "2px"], [2, "padding", "12px 20px", "border-top", "1px solid var(--mj-border-default,#e0e0e0)", "display", "flex", "gap", "8px", "justify-content", "flex-start"], ["mjButton", "", "variant", "primary"], [2, "display", "block", "font-size", "0.875rem", "font-weight", "500", "margin-bottom", "6px"], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.75rem", "white-space", "pre-wrap", "word-break", "break-all", "overflow-y", "auto", "max-height", "240px", "margin", "0"], [2, "background", "var(--mj-bg-surface-sunken,#f0f0f0)", "border-radius", "4px", "padding", "12px", "font-size", "0.75rem", "white-space", "pre-wrap", "word-break", "break-all", "overflow-y", "auto", "max-height", "360px", "margin", "0"], [2, "display", "block", "font-size", "0.875rem", "font-weight", "500", "margin-bottom", "6px", "color", "var(--mj-status-error-text,#c53030)"], [2, "background", "var(--mj-status-error-bg,#fff5f5)", "border", "1px solid var(--mj-status-error-border,#feb2b2)", "border-radius", "4px", "padding", "12px", "font-size", "0.75rem", "white-space", "pre-wrap", "word-break", "break-all", "margin", "0", "color", "var(--mj-status-error-text,#c53030)"]], template: function MCPDashboardComponent_Template(rf, ctx) { if (rf & 1) {
4294
4294
  i0.ɵɵelementStart(0, "mj-page-layout")(1, "mj-page-header", 18)(2, "div", 19);
4295
4295
  i0.ɵɵelement(3, "mj-stat-badge", 20);
4296
4296
  i0.ɵɵelementEnd();
@@ -4299,12 +4299,12 @@ let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
4299
4299
  i0.ɵɵelementStart(6, "mj-filter-panel", 23);
4300
4300
  i0.ɵɵlistener("ValuesChange", function MCPDashboardComponent_Template_mj_filter_panel_ValuesChange_6_listener($event) { return ctx.onFilterValuesChange($event); })("Reset", function MCPDashboardComponent_Template_mj_filter_panel_Reset_6_listener() { return ctx.resetPopoverFilters(); });
4301
4301
  i0.ɵɵelementEnd()();
4302
- i0.ɵɵelementStart(7, "mj-tab-nav", 24);
4303
- i0.ɵɵlistener("TabChange", function MCPDashboardComponent_Template_mj_tab_nav_TabChange_7_listener($event) { return ctx.setActiveTab($event); });
4302
+ i0.ɵɵconditionalCreate(7, MCPDashboardComponent_Case_7_Template, 3, 0, "button", 24)(8, MCPDashboardComponent_Case_8_Template, 3, 0, "button", 24)(9, MCPDashboardComponent_Case_9_Template, 7, 8)(10, MCPDashboardComponent_Case_10_Template, 1, 1, "mj-refresh-button", 25);
4304
4303
  i0.ɵɵelementEnd();
4305
- i0.ɵɵconditionalCreate(8, MCPDashboardComponent_Case_8_Template, 3, 0, "button", 25)(9, MCPDashboardComponent_Case_9_Template, 3, 0, "button", 25)(10, MCPDashboardComponent_Case_10_Template, 7, 8)(11, MCPDashboardComponent_Case_11_Template, 1, 1, "mj-refresh-button", 26);
4304
+ i0.ɵɵelementStart(11, "div", 26)(12, "mj-tab-nav", 27);
4305
+ i0.ɵɵlistener("TabChange", function MCPDashboardComponent_Template_mj_tab_nav_TabChange_12_listener($event) { return ctx.setActiveTab($event); });
4306
4306
  i0.ɵɵelementEnd();
4307
- i0.ɵɵelementStart(12, "div", 27)(13, "mj-page-search", 28);
4307
+ i0.ɵɵelementStart(13, "mj-page-search", 28);
4308
4308
  i0.ɵɵlistener("ValueChange", function MCPDashboardComponent_Template_mj_page_search_ValueChange_13_listener($event) { return ctx.onSearchTermChange($event); });
4309
4309
  i0.ɵɵelementEnd()()();
4310
4310
  i0.ɵɵelementStart(14, "mj-page-body", 29);
@@ -4316,7 +4316,7 @@ let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
4316
4316
  i0.ɵɵconditionalCreate(20, MCPDashboardComponent_Conditional_20_Template, 27, 9, "div", 32);
4317
4317
  i0.ɵɵconditionalCreate(21, MCPDashboardComponent_Conditional_21_Template, 31, 12, "div", 33);
4318
4318
  } if (rf & 2) {
4319
- let tmp_8_0;
4319
+ let tmp_6_0;
4320
4320
  i0.ɵɵadvance(3);
4321
4321
  i0.ɵɵproperty("Count", ctx.CurrentFilteredCount)("Total", ctx.CurrentTotalCount);
4322
4322
  i0.ɵɵadvance(2);
@@ -4324,10 +4324,10 @@ let MCPDashboardComponent = class MCPDashboardComponent extends BaseDashboard {
4324
4324
  i0.ɵɵadvance();
4325
4325
  i0.ɵɵproperty("Fields", ctx.mcpFilterFields)("Values", ctx.mcpFilterValues);
4326
4326
  i0.ɵɵadvance();
4327
+ i0.ɵɵconditional((tmp_6_0 = ctx.ActiveTab) === "servers" ? 7 : tmp_6_0 === "connections" ? 8 : tmp_6_0 === "tools" ? 9 : tmp_6_0 === "logs" ? 10 : -1);
4328
+ i0.ɵɵadvance(5);
4327
4329
  i0.ɵɵproperty("Tabs", ctx.mcpTabs)("ActiveKey", ctx.ActiveTab);
4328
4330
  i0.ɵɵadvance();
4329
- i0.ɵɵconditional((tmp_8_0 = ctx.ActiveTab) === "servers" ? 8 : tmp_8_0 === "connections" ? 9 : tmp_8_0 === "tools" ? 10 : tmp_8_0 === "logs" ? 11 : -1);
4330
- i0.ɵɵadvance(5);
4331
4331
  i0.ɵɵproperty("Value", ctx.CurrentFilters.searchTerm);
4332
4332
  i0.ɵɵadvance();
4333
4333
  i0.ɵɵproperty("Flex", true);
@@ -4351,7 +4351,7 @@ MCPDashboardComponent = __decorate([
4351
4351
  export { MCPDashboardComponent };
4352
4352
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MCPDashboardComponent, [{
4353
4353
  type: Component,
4354
- args: [{ standalone: false, selector: 'mj-mcp-dashboard', template: "<mj-page-layout>\n <!-- Header -->\n <mj-page-header\n Title=\"MCP Dashboard\"\n Icon=\"fa-solid fa-plug-circle-bolt\">\n <div meta>\n <mj-stat-badge [Count]=\"CurrentFilteredCount\" [Total]=\"CurrentTotalCount\" Label=\"items\"></mj-stat-badge>\n </div>\n <div actions>\n <mj-filter-popover\n [ActiveCount]=\"ActiveFilterCount\"\n [ShowClearAll]=\"ActiveFilterCount > 0\"\n (ClearAllRequested)=\"resetPopoverFilters()\">\n <mj-filter-panel\n [Fields]=\"mcpFilterFields\"\n [Values]=\"mcpFilterValues\"\n (ValuesChange)=\"onFilterValuesChange($event)\"\n (Reset)=\"resetPopoverFilters()\">\n </mj-filter-panel>\n </mj-filter-popover>\n\n <!-- Tab Navigation -->\n <mj-tab-nav\n [Tabs]=\"mcpTabs\"\n [ActiveKey]=\"ActiveTab\"\n (TabChange)=\"setActiveTab($any($event))\">\n </mj-tab-nav>\n\n <!-- Action Buttons based on tab -->\n @switch (ActiveTab) {\n @case ('servers') {\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"createServer()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Server\n </button>\n }\n @case ('connections') {\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"createConnection()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Connection\n </button>\n }\n @case ('tools') {\n <mj-view-toggle\n [Options]=\"toolsViewOptions\"\n [ActiveKey]=\"ToolsViewMode\"\n (KeyChange)=\"setToolsViewMode($any($event))\">\n </mj-view-toggle>\n <button mjButton variant=\"secondary\" size=\"sm\"\n (click)=\"toggleScalableMode(!useScalablePagination)\"\n [title]=\"useScalablePagination ? 'Scale mode on: virtual scroll, paginated' : 'Enable scale mode for large tool sets'\">\n <i class=\"fa-solid\" [class.fa-bolt]=\"useScalablePagination\" [class.fa-bolt-lightning]=\"!useScalablePagination\"></i>\n Scale: {{ useScalablePagination ? 'ON' : 'OFF' }}\n </button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"openTestToolDialog()\">\n <i class=\"fa-solid fa-play\"></i>\n Test Tool\n </button>\n }\n @case ('logs') {\n <mj-refresh-button [Loading]=\"IsLoading\" (Clicked)=\"loadAllData()\"></mj-refresh-button>\n }\n }\n </div>\n <div toolbar>\n <mj-page-search\n Placeholder=\"Search\u2026\"\n [Value]=\"CurrentFilters.searchTerm\"\n (ValueChange)=\"onSearchTermChange($event)\">\n </mj-page-search>\n </div>\n </mj-page-header>\n\n <!-- Main Content -->\n <mj-page-body [Flex]=\"true\">\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 @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 mjButton variant=\"primary\" size=\"sm\" (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\">{{ server.Name }}</span>\n <span class=\"status-badge\" [ngClass]=\"getStatusClass(server.Status)\">\n {{ server.Status }}\n </span>\n </div>\n <div class=\"card-actions\" (click)=\"$event.stopPropagation()\">\n <button mjButton variant=\"flat\" (click)=\"editServer(server)\" title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button mjButton variant=\"flat\" (click)=\"deleteServer(server)\" title=\"Delete\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n </div>\n </div>\n <div class=\"card-body\">\n @if (server.Description) {\n <p class=\"description\">{{ server.Description }}</p>\n }\n <div class=\"details-grid\">\n <div class=\"detail\">\n <span class=\"label\">Transport</span>\n <span class=\"value\">{{ server.TransportType }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Auth</span>\n <span class=\"value\">{{ server.DefaultAuthType }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Connections</span>\n <span class=\"value\">{{ server.ConnectionCount }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Tools</span>\n <span class=\"value\">{{ server.ToolCount }}</span>\n </div>\n @if (server.ServerURL) {\n <div class=\"detail full-width\">\n <span class=\"label\">URL</span>\n <span class=\"value url\">{{ server.ServerURL }}</span>\n </div>\n }\n @if (server.Command) {\n <div class=\"detail full-width\">\n <span class=\"label\">Command</span>\n <span class=\"value command\">{{ server.Command }}</span>\n </div>\n }\n <div class=\"detail\">\n <span class=\"label\">Last Sync</span>\n <span class=\"value\">{{ formatDate(server.LastSyncAt) }}</span>\n </div>\n </div>\n </div>\n <!-- Expanded Tools Section -->\n @if (isServerExpanded(server)) {\n <div class=\"expanded-tools-section\">\n <div class=\"tools-section-header\">\n <i class=\"fa-solid fa-wrench\"></i>\n <span>Available Tools ({{ getToolsForServer(server.ID).length }})</span>\n </div>\n @if (getToolsForServer(server.ID).length === 0) {\n <div class=\"no-tools-message\">\n <i class=\"fa-solid fa-info-circle\"></i>\n No tools discovered yet. Sync a connection to discover tools.\n </div>\n } @else {\n <div class=\"tools-mini-list\">\n @for (tool of getToolsForServer(server.ID); track tool.ID) {\n <div class=\"tool-mini-card\">\n <div class=\"tool-mini-info\">\n <span class=\"tool-mini-name\">{{ tool.ToolTitle || tool.ToolName }}</span>\n <span class=\"tool-mini-params\">\n <i class=\"fa-solid fa-sliders\"></i>\n {{ getParamCount(tool) }} params\n </span>\n </div>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"runToolFromCard(tool); $event.stopPropagation()\"\n title=\"Test this tool\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n }\n\n <!-- Connections Tab -->\n @case ('connections') {\n <div class=\"data-grid connections-grid\">\n @if (filteredConnections.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-link\"></i>\n <p>No connections configured</p>\n <button mjButton variant=\"primary\" size=\"sm\" (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\">{{ conn.Name }}</span>\n <span class=\"status-badge\" [ngClass]=\"getStatusClass(conn.Status)\">\n {{ conn.Status }}\n </span>\n </div>\n <div class=\"card-actions\" (click)=\"$event.stopPropagation()\">\n <button mjButton\n variant=\"flat\"\n (click)=\"syncConnectionTools(conn)\"\n [disabled]=\"isSyncing(conn.ID)\"\n title=\"Sync Tools\">\n @if (isSyncing(conn.ID)) {\n <i class=\"fa-solid fa-sync fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-sync\"></i>\n }\n </button>\n <button mjButton variant=\"flat\" (click)=\"editConnection(conn)\" title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button mjButton variant=\"flat\" (click)=\"deleteConnection(conn)\" title=\"Delete\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n </div>\n </div>\n <!-- Sync Progress Banner -->\n @if (isSyncing(conn.ID)) {\n <div class=\"sync-progress-banner\">\n <i class=\"fa-solid fa-circle-notch fa-spin\"></i>\n <span class=\"sync-message\">{{ getSyncProgressMessage(conn.ID) || 'Syncing...' }}</span>\n </div>\n }\n <!-- Sync Result Banner -->\n @if (getSyncState(conn.ID)?.lastResult && !isSyncing(conn.ID)) {\n <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\">{{ conn.Description }}</p>\n }\n <div class=\"details-grid\">\n <div class=\"detail\">\n <span class=\"label\">Server</span>\n <span class=\"value\">{{ conn.ServerName }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Auto Sync</span>\n <span class=\"value\">{{ conn.AutoSyncTools ? 'Yes' : 'No' }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Logging</span>\n <span class=\"value\">{{ conn.LogToolCalls ? 'Enabled' : 'Disabled' }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Last Connected</span>\n <span class=\"value\">{{ formatDate(conn.LastConnectedAt) }}</span>\n </div>\n @if (conn.LastErrorMessage) {\n <div class=\"detail full-width error\">\n <span class=\"label\">Last Error</span>\n <span class=\"value\">{{ conn.LastErrorMessage }}</span>\n </div>\n }\n </div>\n </div>\n <!-- Expanded Tools Section -->\n @if (isConnectionExpanded(conn)) {\n <div class=\"expanded-tools-section\">\n <div class=\"tools-section-header\">\n <i class=\"fa-solid fa-wrench\"></i>\n <span>Available Tools ({{ getToolsForConnection(conn.ID).length }})</span>\n </div>\n @if (getToolsForConnection(conn.ID).length === 0) {\n <div class=\"no-tools-message\">\n <i class=\"fa-solid fa-info-circle\"></i>\n No tools discovered yet. Click the sync button to discover tools.\n </div>\n } @else {\n <div class=\"tools-mini-list\">\n @for (tool of getToolsForConnection(conn.ID); track tool.ID) {\n <div class=\"tool-mini-card\">\n <div class=\"tool-mini-info\">\n <span class=\"tool-mini-name\">{{ tool.ToolTitle || tool.ToolName }}</span>\n <span class=\"tool-mini-params\">\n <i class=\"fa-solid fa-sliders\"></i>\n {{ getParamCount(tool) }} params\n </span>\n </div>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"runToolFromCard(tool, conn); $event.stopPropagation()\"\n title=\"Test this tool with this connection\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n }\n\n <!-- Tools Tab -->\n @case ('tools') {\n @if (useScalablePagination) {\n <div style=\"padding:8px 12px;font-size:0.8rem;color:var(--mj-text-secondary,#475569);background:var(--mj-bg-surface-card,#f8f9fa);border-radius:4px;margin-bottom:6px\">\n Showing <strong>{{ visiblePagedTools.length }}</strong> of <strong>{{ scaleDenominator() }}</strong> tools\n </div>\n <div style=\"height:calc(100vh - 260px);width:100%;overflow-y:auto;border:1px solid var(--mj-border-subtle,#eee);border-radius:4px\" (scroll)=\"onToolsScrollNative($event)\">\n @for (tool of visiblePagedTools; track tool.ID) {\n <div style=\"display:flex;align-items:center;gap:12px;padding:12px 14px;border-bottom:1px solid var(--mj-border-subtle,#eee);height:72px;box-sizing:border-box\">\n <i class=\"fa-solid fa-wrench\" style=\"color:var(--mj-brand-primary,#264FAF)\"></i>\n <div style=\"flex:1;min-width:0\">\n <div style=\"font-weight:500;font-size:0.875rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ tool.ToolTitle || tool.ToolName }}</div>\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ tool.ServerName }} \u00B7 {{ tool.ToolName }}</div>\n </div>\n <button type=\"button\" (click)=\"toggleFavorite(tool.ID, $event)\" [title]=\"isFavorited(tool.ID) ? 'Unfavorite' : 'Favorite'\" style=\"background:transparent;border:none;cursor:pointer;padding:4px;font-size:1rem\" [style.color]=\"isFavorited(tool.ID) ? 'var(--mj-status-warning, #f59e0b)' : 'var(--mj-text-muted, #bbb)'\">\n <i [class]=\"isFavorited(tool.ID) ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(tool.Status)\">{{ tool.Status }}</span>\n </div>\n }\n @if (toolsLoading) {\n <div style=\"padding:12px;text-align:center;font-size:0.8rem;color:var(--mj-text-muted,#888)\">Loading more\u2026</div>\n }\n </div>\n } @else if (ServerGroups.length === 0) {\n <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\">{{ getServerToolCount(group.server.ID) }} tools</span>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(group.server.Status)\">\n {{ group.server.Status }}\n </span>\n </div>\n <div class=\"server-actions\" (click)=\"$event.stopPropagation()\">\n <button mjButton variant=\"flat\" (click)=\"openTestToolDialog(undefined, undefined)\" title=\"Test a tool\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n </div>\n </div>\n\n <!-- Tools Content -->\n @if (group.expanded) {\n <!-- Card View -->\n @if (ToolsViewMode === 'card') {\n <div class=\"tools-grid\">\n @for (tool of group.tools; track tool.ID) {\n <div class=\"tool-card\" [class.expanded]=\"isToolExpanded(tool)\">\n <div class=\"tool-card-header\" (click)=\"toggleToolExpand(tool)\">\n <div class=\"tool-title\">\n <i class=\"fa-solid fa-wrench\"></i>\n <span class=\"name\">{{ tool.ToolTitle || tool.ToolName }}</span>\n </div>\n <div class=\"tool-meta\">\n <span class=\"param-badge\" title=\"Parameters\">\n <i class=\"fa-solid fa-sliders\"></i>\n {{ getParamCount(tool) }}\n </span>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(tool.Status)\">\n {{ tool.Status }}\n </span>\n </div>\n </div>\n @if (tool.ToolDescription) {\n <p class=\"tool-description\">{{ tool.ToolDescription }}</p>\n }\n <div class=\"tool-card-actions\">\n <button type=\"button\" (click)=\"toggleFavorite(tool.ID, $event)\" [title]=\"isFavorited(tool.ID) ? 'Unfavorite' : 'Favorite'\" style=\"background:transparent;border:none;cursor:pointer;padding:4px;font-size:1rem\" [style.color]=\"isFavorited(tool.ID) ? 'var(--mj-status-warning, #f59e0b)' : 'var(--mj-text-muted, #bbb)'\">\n <i [class]=\"isFavorited(tool.ID) ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"openTestToolDialog(tool)\" title=\"Test this tool\">\n <i class=\"fa-solid fa-play\"></i>\n Test\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"toggleToolExpand(tool)\" title=\"View details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!isToolExpanded(tool)\" [class.fa-chevron-up]=\"isToolExpanded(tool)\"></i>\n {{ isToolExpanded(tool) ? 'Less' : 'More' }}\n </button>\n </div>\n <!-- Expanded Details -->\n @if (isToolExpanded(tool)) {\n <div class=\"tool-details\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Tool Name:</span>\n <span class=\"detail-value mono\">{{ tool.ToolName }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Parameters:</span>\n <span class=\"detail-value\">\n {{ getParamCount(tool) }} total, {{ getRequiredParamCount(tool) }} required\n </span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Discovered:</span>\n <span class=\"detail-value\">{{ formatDate(tool.DiscoveredAt) }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Last Seen:</span>\n <span class=\"detail-value\">{{ formatDate(tool.LastSeenAt) }}</span>\n </div>\n @if (tool.InputSchema) {\n <div class=\"detail-row full\">\n <span class=\"detail-label\">Input Schema:</span>\n <pre class=\"schema-preview\">{{ getFormattedInputSchema(tool) }}</pre>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n } @else {\n <!-- List View -->\n <div class=\"tools-list\">\n <table>\n <thead>\n <tr>\n <th>Tool</th>\n <th>Description</th>\n <th>Params</th>\n <th>Status</th>\n <th>Last Seen</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (tool of group.tools; track tool.ID) {\n <tr [class.expanded]=\"isToolExpanded(tool)\">\n <td class=\"tool-name-cell\">\n <i class=\"fa-solid fa-wrench\"></i>\n <div class=\"tool-name-info\">\n <span class=\"tool-title\">{{ tool.ToolTitle || tool.ToolName }}</span>\n @if (tool.ToolTitle) {\n <span class=\"tool-code\">{{ tool.ToolName }}</span>\n }\n </div>\n </td>\n <td class=\"description-cell\">{{ tool.ToolDescription || '-' }}</td>\n <td>\n <span class=\"param-count\">{{ getParamCount(tool) }}</span>\n </td>\n <td>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(tool.Status)\">\n {{ tool.Status }}\n </span>\n </td>\n <td>{{ formatDate(tool.LastSeenAt) }}</td>\n <td class=\"actions-cell\">\n <button type=\"button\" (click)=\"toggleFavorite(tool.ID, $event)\" [title]=\"isFavorited(tool.ID) ? 'Unfavorite' : 'Favorite'\" style=\"background:transparent;border:none;cursor:pointer;padding:4px;font-size:0.95rem;margin-right:4px\" [style.color]=\"isFavorited(tool.ID) ? 'var(--mj-status-warning, #f59e0b)' : 'var(--mj-text-muted, #bbb)'\">\n <i [class]=\"isFavorited(tool.ID) ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"openTestToolDialog(tool)\" title=\"Test\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"toggleToolExpand(tool)\" title=\"Details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!isToolExpanded(tool)\" [class.fa-chevron-up]=\"isToolExpanded(tool)\"></i>\n </button>\n </td>\n </tr>\n @if (isToolExpanded(tool)) {\n <tr class=\"detail-row-expanded\">\n <td colspan=\"6\">\n <div class=\"inline-details\">\n <div class=\"detail-section\">\n <span class=\"detail-label\">Tool Name:</span>\n <span class=\"detail-value mono\">{{ tool.ToolName }}</span>\n </div>\n <div class=\"detail-section\">\n <span class=\"detail-label\">Discovered:</span>\n <span class=\"detail-value\">{{ formatDate(tool.DiscoveredAt) }}</span>\n </div>\n <div class=\"detail-section\">\n <span class=\"detail-label\">Required Params:</span>\n <span class=\"detail-value\">{{ getRequiredParamCount(tool) }}</span>\n </div>\n @if (tool.InputSchema) {\n <div class=\"detail-section full\">\n <span class=\"detail-label\">Input Schema:</span>\n <pre class=\"schema-preview\">{{ getFormattedInputSchema(tool) }}</pre>\n </div>\n }\n </div>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n }\n }\n </div>\n }\n </div>\n }\n }\n\n <!-- Logs Tab -->\n @case ('logs') {\n <div class=\"data-table\">\n @if (filteredLogs.length === 0) {\n <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 </mj-page-body>\n</mj-page-layout>\n\n<!-- Server Dialog (inline \u2014 avoids sub-component DI issues) -->\n@if (ShowServerDialog) {\n <div style=\"position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\" (click)=\"cancelServerForm()\">\n <div style=\"display:flex;flex-direction:column;background:var(--mj-bg-surface,#fff);border-radius:8px;box-shadow:0 20px 60px rgba(0,0,0,0.3);overflow:hidden;width:600px;max-width:90vw;max-height:90vh\" (click)=\"$event.stopPropagation()\">\n <!-- Title bar -->\n <div style=\"display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <h2 style=\"margin:0;font-size:1.125rem;font-weight:600;color:var(--mj-text-primary,#333)\">{{ EditingServer ? 'Edit MCP Server' : 'Add MCP Server' }}</h2>\n <button style=\"display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:none;cursor:pointer;color:var(--mj-text-muted,#888);border-radius:4px\" (click)=\"cancelServerForm()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Body -->\n <div style=\"flex:1;overflow-y:auto;padding:20px;color:var(--mj-text-primary,#333)\">\n @if (ServerFormError) {\n <div style=\"background:var(--mj-status-error-bg,#fff5f5);border:1px solid var(--mj-status-error-border,#feb2b2);border-radius:6px;padding:10px 14px;margin-bottom:16px;color:var(--mj-status-error-text,#c53030);font-size:0.875rem\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i> {{ ServerFormError }}\n </div>\n }\n <!-- Name -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Name <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #sfName class=\"mj-input\" placeholder=\"e.g., GitHub MCP Server\" [value]=\"ServerForm.Name\" (input)=\"ServerForm.Name = sfName.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n <!-- Description -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Description</label>\n <textarea #sfDesc class=\"mj-textarea\" placeholder=\"Optional description\" [value]=\"ServerForm.Description\" (input)=\"ServerForm.Description = sfDesc.value\" rows=\"3\" style=\"width:100%;box-sizing:border-box\"></textarea>\n </div>\n <!-- Transport Type -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Transport Type <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #sfTransport class=\"mj-input\" [value]=\"ServerForm.TransportType\" (change)=\"ServerForm.TransportType = sfTransport.value\" style=\"width:100%;box-sizing:border-box\">\n @for (t of TransportTypes; track t.value) {\n <option [value]=\"t.value\">{{ t.label }}</option>\n }\n </select>\n </div>\n <!-- Server URL (for HTTP-based transports) -->\n @if (ServerForm.TransportType === 'StreamableHTTP' || ServerForm.TransportType === 'SSE' || ServerForm.TransportType === 'WebSocket') {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Server URL <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #sfUrl class=\"mj-input\" placeholder=\"https://api.example.com/mcp\" [value]=\"ServerForm.ServerURL\" (input)=\"ServerForm.ServerURL = sfUrl.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n }\n <!-- Command (for Stdio) -->\n @if (ServerForm.TransportType === 'Stdio') {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Command <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #sfCmd class=\"mj-input\" placeholder=\"e.g., npx\" [value]=\"ServerForm.Command\" (input)=\"ServerForm.Command = sfCmd.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n }\n <!-- Auth Type -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Default Auth Type</label>\n <select #sfAuth class=\"mj-input\" [value]=\"ServerForm.DefaultAuthType\" (change)=\"ServerForm.DefaultAuthType = sfAuth.value\" style=\"width:100%;box-sizing:border-box\">\n @for (a of AuthTypes; track a.value) {\n <option [value]=\"a.value\">{{ a.label }}</option>\n }\n </select>\n </div>\n <!-- Status -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Status</label>\n <select #sfStatus class=\"mj-input\" [value]=\"ServerForm.Status\" (change)=\"ServerForm.Status = sfStatus.value\" style=\"width:100%;box-sizing:border-box\">\n <option value=\"Active\">Active</option>\n <option value=\"Inactive\">Inactive</option>\n </select>\n </div>\n </div>\n <!-- Actions -->\n <div style=\"display:flex;align-items:center;gap:8px;padding:16px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <button mjButton variant=\"primary\" (click)=\"saveServerForm()\" [disabled]=\"ServerFormSaving\">\n @if (ServerFormSaving) { <i class=\"fa-solid fa-spinner fa-spin\"></i> }\n {{ EditingServer ? 'Update' : 'Create' }}\n </button>\n <button mjButton (click)=\"cancelServerForm()\">Cancel</button>\n </div>\n </div>\n </div>\n}\n\n<!-- Connection Dialog (inline) -->\n@if (ShowConnectionDialog) {\n <div style=\"position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\" (click)=\"cancelConnectionForm()\">\n <div style=\"display:flex;flex-direction:column;background:var(--mj-bg-surface,#fff);border-radius:8px;box-shadow:0 20px 60px rgba(0,0,0,0.3);overflow:hidden;width:560px;max-width:90vw;max-height:90vh\" (click)=\"$event.stopPropagation()\">\n <div style=\"display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <h2 style=\"margin:0;font-size:1.125rem;font-weight:600;color:var(--mj-text-primary,#333)\">Add Connection</h2>\n <button style=\"display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:none;cursor:pointer;color:var(--mj-text-muted,#888);border-radius:4px\" (click)=\"cancelConnectionForm()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div style=\"flex:1;overflow-y:auto;padding:20px;color:var(--mj-text-primary,#333)\">\n @if (ConnectionFormError) {\n <div style=\"background:var(--mj-status-error-bg,#fff5f5);border:1px solid var(--mj-status-error-border,#feb2b2);border-radius:6px;padding:10px 14px;margin-bottom:16px;color:var(--mj-status-error-text,#c53030);font-size:0.875rem\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i> {{ ConnectionFormError }}\n </div>\n }\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Server <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #cfServer class=\"mj-input\" [value]=\"ConnectionForm.MCPServerID\" (change)=\"ConnectionForm.MCPServerID = cfServer.value\" style=\"width:100%;box-sizing:border-box\">\n <option value=\"\">\u2014 select server \u2014</option>\n @for (s of servers; track s.ID) {\n <option [value]=\"s.ID\">{{ s.Name }}</option>\n }\n </select>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Name <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #cfName class=\"mj-input\" placeholder=\"e.g., Zapier Connection\" [value]=\"ConnectionForm.Name\" (input)=\"ConnectionForm.Name = cfName.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Bearer Token</label>\n <input #cfToken class=\"mj-input\" placeholder=\"Paste bearer token here\" [value]=\"ConnectionForm.BearerToken\" (input)=\"ConnectionForm.BearerToken = cfToken.value\" style=\"width:100%;box-sizing:border-box;font-family:monospace;font-size:0.8rem\" />\n <span style=\"font-size:0.75rem;color:var(--mj-text-muted,#888)\">Will be stored as a Credential and linked to this connection</span>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Status</label>\n <select #cfStatus class=\"mj-input\" [value]=\"ConnectionForm.Status\" (change)=\"ConnectionForm.Status = cfStatus.value\" style=\"width:100%;box-sizing:border-box\">\n <option value=\"Active\">Active</option>\n <option value=\"Inactive\">Inactive</option>\n </select>\n </div>\n </div>\n <div style=\"display:flex;align-items:center;gap:8px;padding:16px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <button mjButton variant=\"primary\" (click)=\"saveConnectionForm()\" [disabled]=\"ConnectionFormSaving\">\n @if (ConnectionFormSaving) { <i class=\"fa-solid fa-spinner fa-spin\"></i> }\n Create\n </button>\n <button mjButton (click)=\"cancelConnectionForm()\">Cancel</button>\n </div>\n </div>\n </div>\n}\n\n<!-- Test Tool Dialog (inline) -->\n@if (ShowTestToolDialog) {\n <div style=\"position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\" (click)=\"testCloseDialog()\">\n <div style=\"display:flex;flex-direction:column;background:var(--mj-bg-surface,#fff);border-radius:8px;box-shadow:0 20px 60px rgba(0,0,0,0.3);overflow:hidden;width:640px;max-width:90vw;max-height:90vh\" (click)=\"$event.stopPropagation()\">\n <!-- Title bar -->\n <div style=\"display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <h2 style=\"margin:0;font-size:1.125rem;font-weight:600;color:var(--mj-text-primary,#333)\">\n Test MCP Tool\n @if (TestStep !== 'select') {\n <span style=\"font-size:0.875rem;font-weight:400;color:var(--mj-text-muted,#888);margin-left:8px\">\u2014 {{ TestSelectedTool?.ToolTitle || TestSelectedTool?.ToolName }}</span>\n }\n </h2>\n <button style=\"display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:none;cursor:pointer;color:var(--mj-text-muted,#888);border-radius:4px\" (click)=\"testCloseDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Step indicator -->\n <div style=\"display:flex;gap:0;padding:10px 20px;background:var(--mj-bg-surface-card,#f9f9f9);border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <span style=\"font-size:0.8rem;font-weight:500;\" [style.color]=\"TestStep==='select' ? 'var(--mj-brand-primary,#264FAF)' : 'var(--mj-text-muted,#888)'\">1. Select</span>\n <span style=\"margin:0 8px;color:var(--mj-text-muted,#888)\">\u203A</span>\n <span style=\"font-size:0.8rem;font-weight:500;\" [style.color]=\"TestStep==='configure' ? 'var(--mj-brand-primary,#264FAF)' : 'var(--mj-text-muted,#888)'\">2. Configure</span>\n <span style=\"margin:0 8px;color:var(--mj-text-muted,#888)\">\u203A</span>\n <span style=\"font-size:0.8rem;font-weight:500;\" [style.color]=\"TestStep==='results' ? 'var(--mj-brand-primary,#264FAF)' : 'var(--mj-text-muted,#888)'\">3. Results</span>\n </div>\n <!-- Body -->\n <div style=\"flex:1;overflow-y:auto;padding:20px;color:var(--mj-text-primary,#333)\">\n @if (TestStep === 'select') {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Server <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #tSrv class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (change)=\"onTestServerChange(tSrv.value)\">\n <option value=\"\">\u2014 select server \u2014</option>\n @for (s of servers; track s.ID) {\n <option [value]=\"s.ID\" [selected]=\"TestToolServerID === s.ID\">{{ s.Name }}</option>\n }\n </select>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Connection <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #tConn class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" [attr.disabled]=\"!TestToolServerID ? true : null\" (change)=\"onTestConnectionChange(tConn.value)\">\n <option value=\"\">\u2014 select connection \u2014</option>\n @for (c of TestFilteredConnections; track c.ID) {\n <option [value]=\"c.ID\" [selected]=\"TestToolConnectionID === c.ID\">{{ c.Name }}</option>\n }\n </select>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Tool <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #tSearch type=\"text\" class=\"mj-input\" style=\"width:100%;box-sizing:border-box;margin-bottom:6px\" placeholder=\"Search tools\u2026 (Recently used appear first)\" [value]=\"TestToolSearch\" (input)=\"onTestToolSearchChange(tSearch.value)\" [attr.disabled]=\"!TestToolServerID ? true : null\" />\n <div style=\"max-height:220px;overflow-y:auto;border:1px solid var(--mj-border-default,#e0e0e0);border-radius:4px;background:var(--mj-bg-surface,#fff)\">\n @if (TestComboboxTools.length === 0) {\n <div style=\"padding:12px;text-align:center;font-size:0.8rem;color:var(--mj-text-muted,#888)\">No tools match.</div>\n }\n @for (t of TestComboboxTools; track t.ID) {\n <div (click)=\"pickTestTool(t.ID)\" style=\"padding:8px 12px;cursor:pointer;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--mj-border-subtle,#eee)\" [style.background]=\"TestToolID === t.ID ? 'color-mix(in srgb, var(--mj-brand-primary,#264FAF) 12%, var(--mj-bg-surface,#fff))' : null\">\n @if (isRecentTestTool(t.ID)) {\n <span style=\"font-size:0.65rem;background:var(--mj-brand-primary,#264FAF);color:#fff;padding:1px 6px;border-radius:8px;font-weight:600\">Recent</span>\n }\n <div style=\"flex:1;min-width:0\">\n <div style=\"font-size:0.85rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ t.ToolTitle || t.ToolName }}</div>\n @if (t.ToolTitle && t.ToolName !== t.ToolTitle) {\n <div style=\"font-size:0.7rem;color:var(--mj-text-muted,#888);overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ t.ToolName }}</div>\n }\n </div>\n @if (TestToolID === t.ID) {\n <i class=\"fa-solid fa-check\" style=\"color:var(--mj-brand-primary,#264FAF)\"></i>\n }\n </div>\n }\n </div>\n </div>\n } @else if (TestStep === 'configure') {\n @if (TestParamConfigs.length === 0) {\n <div style=\"text-align:center;padding:24px;color:var(--mj-text-muted,#888)\">\n <i class=\"fa-solid fa-check-circle\" style=\"font-size:2rem;margin-bottom:8px;display:block\"></i>\n <p>This tool requires no parameters.</p>\n </div>\n } @else {\n @for (p of TestParamConfigs; track p.name) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">\n {{ p.name }} @if (p.required) { <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span> }\n </label>\n @if (p.description) {\n <p style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin:0 0 4px 0\">{{ p.description }}</p>\n }\n @if (p.enumValues.length > 0) {\n <select #enumEl class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (change)=\"testSetParam(p.name, enumEl.value)\">\n <option value=\"\">\u2014 select \u2014</option>\n @for (v of p.enumValues; track $index) { <option [value]=\"$any(v)\">{{ $any(v) }}</option> }\n </select>\n } @else if (p.type === 'boolean') {\n <select #boolEl class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (change)=\"testSetParam(p.name, boolEl.value)\">\n <option value=\"false\">false</option>\n <option value=\"true\">true</option>\n </select>\n } @else if (p.type === 'array' || p.type === 'object') {\n <textarea #jsonEl class=\"mj-textarea\" rows=\"3\" style=\"width:100%;box-sizing:border-box;font-family:monospace;font-size:0.8rem\" (input)=\"testSetParam(p.name, jsonEl.value)\" [placeholder]=\"'Enter JSON ' + p.type + '...'\"></textarea>\n } @else {\n <input #strEl class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (input)=\"testSetParam(p.name, strEl.value)\" [placeholder]=\"p.description || 'Enter value...'\" />\n }\n </div>\n }\n }\n } @else {\n @if (TestExecutionResult?.Success) {\n <div style=\"background:var(--mj-status-success-bg,#f0fff4);border:1px solid var(--mj-status-success-border,#9ae6b4);border-radius:6px;padding:12px 16px;margin-bottom:16px;color:var(--mj-status-success-text,#276749)\">\n <i class=\"fa-solid fa-check-circle\"></i> Execution Successful\n @if (TestExecutionResult?.DurationMs) { <span style=\"float:right;font-size:0.75rem\">{{ TestExecutionResult?.DurationMs }}ms</span> }\n </div>\n } @else {\n <div style=\"background:var(--mj-status-error-bg,#fff5f5);border:1px solid var(--mj-status-error-border,#feb2b2);border-radius:6px;padding:12px 16px;margin-bottom:16px;color:var(--mj-status-error-text,#c53030)\">\n <i class=\"fa-solid fa-times-circle\"></i> Execution Failed\n </div>\n }\n @if (TestExecutionResult?.ErrorMessage) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:4px\">Error</label>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.8rem;white-space:pre-wrap;word-break:break-all;margin:0\">{{ TestExecutionResult?.ErrorMessage }}</pre>\n </div>\n }\n @if (TestExecutionResult?.Result) {\n <div>\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:4px\">Result</label>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.8rem;white-space:pre-wrap;word-break:break-all;overflow-y:auto;max-height:300px;margin:0\">{{ formatTestResult(TestExecutionResult?.Result) }}</pre>\n </div>\n }\n }\n </div>\n <!-- Actions -->\n <div style=\"display:flex;align-items:center;gap:8px;padding:16px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n @if (TestStep === 'select') {\n <button mjButton variant=\"primary\" (click)=\"testProceedToConfig()\" [disabled]=\"!TestCanProceed\">Next <i class=\"fa-solid fa-arrow-right\"></i></button>\n <button mjButton (click)=\"testCloseDialog()\">Cancel</button>\n } @else if (TestStep === 'configure') {\n <button mjButton variant=\"primary\" (click)=\"testExecuteTool()\" [disabled]=\"!TestIsValid || TestIsExecuting\">\n @if (TestIsExecuting) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Executing... } @else { <i class=\"fa-solid fa-play\"></i> Execute }\n </button>\n <button mjButton (click)=\"testGoBack()\"><i class=\"fa-solid fa-arrow-left\"></i> Back</button>\n } @else {\n <button mjButton variant=\"primary\" (click)=\"testRunAgain()\"><i class=\"fa-solid fa-redo\"></i> Run Again</button>\n <button mjButton (click)=\"testGoBack()\">Edit Params</button>\n <button mjButton (click)=\"testCloseDialog()\">Close</button>\n }\n </div>\n </div>\n </div>\n}\n\n<!-- Log Detail Panel (inlined) -->\n@if (ShowLogDetailPanel && SelectedLog) {\n <div style=\"position:fixed;inset:0;z-index:10000;pointer-events:none\">\n <div style=\"position:absolute;inset:0;background:rgba(0,0,0,0.35);pointer-events:auto\" (click)=\"onLogDetailClose()\"></div>\n <div style=\"position:absolute;top:0;right:0;height:100%;width:560px;max-width:95vw;background:var(--mj-bg-surface,#fff);box-shadow:-4px 0 20px rgba(0,0,0,0.15);display:flex;flex-direction:column;pointer-events:auto\">\n <div style=\"padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);display:flex;align-items:center;justify-content:space-between\">\n <div>\n <h3 style=\"margin:0;font-size:1rem;font-weight:600\">{{ SelectedLog.ToolName }}</h3>\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin-top:4px\">\n <span [class]=\"SelectedLog.Status === 'Success' ? 'status-success' : SelectedLog.Status === 'Error' ? 'status-error' : 'status-running'\" style=\"font-weight:600\">{{ SelectedLog.Status }}</span>\n <span style=\"margin:0 8px\">\u00B7</span>\n <span>{{ formatDate(SelectedLog.StartedAt) }}</span>\n @if (SelectedLog.DurationMs != null) {\n <span style=\"margin:0 8px\">\u00B7</span>\n <span>{{ formatDuration(SelectedLog.DurationMs) }}</span>\n }\n </div>\n </div>\n <button mjButton (click)=\"onLogDetailClose()\" aria-label=\"Close\"><i class=\"fa-solid fa-xmark\"></i></button>\n </div>\n <div style=\"flex:1;overflow-y:auto;padding:16px 20px\">\n <div style=\"margin-bottom:16px\">\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin-bottom:4px\">Connection</div>\n <div style=\"font-size:0.875rem\">{{ SelectedLog.ConnectionName || SelectedLog.ConnectionID }}</div>\n @if (SelectedLog.ServerName) {\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin-top:2px\">{{ SelectedLog.ServerName }}</div>\n }\n </div>\n @if (SelectedLog.InputArgs) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:6px\">Input Arguments</label>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;overflow-y:auto;max-height:240px;margin:0\">{{ formatLogJson(SelectedLog.InputArgs) }}</pre>\n </div>\n }\n @if (SelectedLog.Result) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:6px\">Result</label>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;overflow-y:auto;max-height:360px;margin:0\">{{ formatLogJson(SelectedLog.Result) }}</pre>\n </div>\n }\n @if (SelectedLog.ErrorMessage) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:6px;color:var(--mj-status-error-text,#c53030)\">Error</label>\n <pre style=\"background:var(--mj-status-error-bg,#fff5f5);border:1px solid var(--mj-status-error-border,#feb2b2);border-radius:4px;padding:12px;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;margin:0;color:var(--mj-status-error-text,#c53030)\">{{ SelectedLog.ErrorMessage }}</pre>\n </div>\n }\n </div>\n <div style=\"padding:12px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);display:flex;gap:8px;justify-content:flex-start\">\n @if (SelectedLog.ToolID) {\n <button mjButton variant=\"primary\" (click)=\"onRunAgainFromLog({ toolId: SelectedLog.ToolID!, connectionId: SelectedLog.ConnectionID })\"><i class=\"fa-solid fa-redo\"></i> Run Again</button>\n }\n <button mjButton (click)=\"onLogDetailClose()\">Close</button>\n </div>\n </div>\n </div>\n}\n", styles: ["/* MCP Dashboard - Header + Filter Panel Layout (outer shell now provided by <mj-page-layout>) */\n\n/* Tab navigation now provided by <mj-tab-nav>. */\n\n/* ========================================\n Main Content Area\n ======================================== */\n/* Error Banner */\n.error-banner {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n margin: 0 0 16px 0;\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 now provided by <mj-view-toggle>.\n ======================================== */\n\n/* ========================================\n Tools Tab - Server Groups\n ======================================== */\n.tools-container {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.server-group {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n\n.server-group-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.server-group-header:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.server-group.collapsed .server-group-header {\n border-bottom: none;\n}\n\n.server-info {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.expand-icon {\n font-size: 12px;\n color: var(--mj-text-disabled);\n transition: transform 0.2s ease;\n}\n\n.expand-icon.expanded {\n transform: rotate(90deg);\n}\n\n.server-name {\n font-weight: 600;\n font-size: 15px;\n color: var(--mj-text-primary);\n}\n\n.tool-count {\n font-size: 12px;\n color: var(--mj-text-secondary);\n background: rgba(0, 0, 0, 0.05);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.status-badge.small {\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.server-actions {\n display: flex;\n gap: 4px;\n}\n\n/* ========================================\n Tools Grid (Card View)\n ======================================== */\n.tools-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 12px;\n padding: 16px;\n}\n\n.tool-card {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n transition: all 0.2s ease;\n overflow: hidden;\n}\n\n.tool-card:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.tool-card.expanded {\n border-color: var(--mj-brand-primary);\n}\n\n.tool-card-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n padding: 12px 14px;\n cursor: pointer;\n}\n\n.tool-title {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n}\n\n.tool-title i {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n.tool-title .name {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.tool-meta {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.param-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: var(--mj-bg-surface-card);\n border-radius: 10px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.param-badge i {\n font-size: 10px;\n}\n\n.tool-description {\n margin: 0;\n padding: 0 14px 8px 14px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.tool-card-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 8px 14px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n/* Tool Details (Expanded) */\n.tool-details {\n padding: 12px 14px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n}\n\n.tool-details .detail-row {\n display: flex;\n padding: 6px 0;\n font-size: 12px;\n}\n\n.tool-details .detail-row.full {\n flex-direction: column;\n gap: 6px;\n}\n\n.tool-details .detail-label {\n min-width: 100px;\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.tool-details .detail-value {\n color: var(--mj-text-primary);\n}\n\n.tool-details .detail-value.mono {\n font-family: 'Consolas', 'Monaco', monospace;\n background: var(--mj-bg-surface);\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid var(--mj-border-default);\n}\n\n.schema-preview {\n margin: 0;\n padding: 10px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-family: 'Consolas', 'Monaco', monospace;\n font-size: 11px;\n line-height: 1.4;\n max-height: 200px;\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ========================================\n Tools List View\n ======================================== */\n.tools-list {\n overflow: auto;\n}\n\n.tools-list table {\n width: 100%;\n border-collapse: collapse;\n}\n\n.tools-list th,\n.tools-list td {\n padding: 10px 14px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.tools-list th {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n font-weight: 600;\n background: var(--mj-bg-surface-card);\n position: sticky;\n top: 0;\n}\n\n.tools-list tbody tr {\n transition: background 0.15s ease;\n}\n\n.tools-list tbody tr:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.tools-list tbody tr.expanded {\n background: color-mix(in srgb, var(--mj-brand-primary) 4%, transparent);\n}\n\n.tool-name-cell {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.tool-name-cell i {\n color: var(--mj-brand-primary);\n margin-top: 2px;\n}\n\n.tool-name-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.tool-name-info .tool-title {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.tool-name-info .tool-code {\n font-size: 11px;\n font-family: monospace;\n color: var(--mj-text-secondary);\n}\n\n.description-cell {\n max-width: 300px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.param-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n background: var(--mj-bg-surface-card);\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.actions-cell {\n white-space: nowrap;\n}\n\n/* Expanded Detail Row in List View */\n.detail-row-expanded td {\n padding: 0;\n background: var(--mj-bg-surface-card);\n}\n\n.inline-details {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n padding: 14px;\n border-top: 1px dashed var(--mj-border-default);\n}\n\n.inline-details .detail-section {\n display: flex;\n gap: 8px;\n font-size: 12px;\n}\n\n.inline-details .detail-section.full {\n flex-basis: 100%;\n flex-direction: column;\n gap: 6px;\n}\n\n.inline-details .detail-label {\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.inline-details .detail-value {\n color: var(--mj-text-primary);\n}\n\n.inline-details .detail-value.mono {\n font-family: monospace;\n}\n\n/* ========================================\n Responsive Styles\n ======================================== */\n@media (max-width: 900px) {\n .mcp-dashboard {\n flex-direction: column;\n }\n\n .sidebar {\n width: 100%;\n min-width: 100%;\n flex-direction: row;\n height: auto;\n border-right: none;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .sidebar-header {\n padding: 12px 16px;\n border-bottom: none;\n border-right: 1px solid var(--mj-border-default);\n }\n\n .sidebar-nav {\n flex-direction: row;\n padding: 8px;\n overflow-x: auto;\n flex: 1;\n }\n\n .nav-item {\n padding: 8px 12px;\n margin-bottom: 0;\n margin-right: 4px;\n white-space: nowrap;\n }\n\n .sidebar-stats {\n display: none;\n }\n\n .sidebar-footer {\n border-top: none;\n border-left: 1px solid var(--mj-border-default);\n padding: 8px;\n }\n\n .data-grid {\n grid-template-columns: 1fr;\n }\n\n .header-actions {\n flex-wrap: wrap;\n }\n\n .search-box {\n min-width: 100%;\n order: 1;\n }\n}\n\n@media (max-width: 600px) {\n .content-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n\n .header-actions {\n width: 100%;\n }\n\n .search-box {\n width: 100%;\n }\n}\n\n/* ========================================\n Clickable Log Rows\n ======================================== */\n.clickable-row {\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.clickable-row:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent) !important;\n}\n\n.clickable-row .action-cell {\n color: var(--mj-border-strong);\n transition: color 0.15s ease;\n}\n\n.clickable-row:hover .action-cell {\n color: var(--mj-brand-primary);\n}\n\n.action-cell {\n width: 40px;\n text-align: center;\n}\n\n/* ========================================\n Expandable Card Styles\n ======================================== */\n.card-header.clickable {\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.card-header.clickable:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.expand-arrow {\n font-size: 12px;\n color: var(--mj-text-disabled);\n transition: transform 0.2s ease;\n margin-right: 4px;\n}\n\n.expand-arrow.rotated {\n transform: rotate(90deg);\n}\n\n.data-card.expanded {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n/* ========================================\n Expanded Tools Section (Server/Connection Cards)\n ======================================== */\n.expanded-tools-section {\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.tools-section-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.tools-section-header i {\n font-size: 14px;\n}\n\n.no-tools-message {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.no-tools-message i {\n color: var(--mj-text-disabled);\n}\n\n/* Tools Mini List */\n.tools-mini-list {\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: 300px;\n overflow-y: auto;\n}\n\n.tool-mini-card {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n transition: all 0.15s ease;\n}\n\n.tool-mini-card:hover {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 2%, transparent);\n}\n\n.tool-mini-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n}\n\n.tool-mini-name {\n font-weight: 500;\n font-size: 13px;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tool-mini-params {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.tool-mini-params i {\n font-size: 10px;\n}\n\n.tool-mini-card button {\n flex-shrink: 0;\n margin-left: 8px;\n}\n\n/* Search Highlight */\n:host ::ng-deep .search-highlight,\n.search-highlight {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: inherit;\n}\n"] }]
4354
+ args: [{ standalone: false, selector: 'mj-mcp-dashboard', template: "<mj-page-layout>\n <!-- Header -->\n <mj-page-header\n Title=\"MCP Dashboard\"\n Icon=\"fa-solid fa-plug-circle-bolt\">\n <div meta>\n <mj-stat-badge [Count]=\"CurrentFilteredCount\" [Total]=\"CurrentTotalCount\" Label=\"items\"></mj-stat-badge>\n </div>\n <div actions>\n <mj-filter-popover\n [ActiveCount]=\"ActiveFilterCount\"\n [ShowClearAll]=\"ActiveFilterCount > 0\"\n (ClearAllRequested)=\"resetPopoverFilters()\">\n <mj-filter-panel\n [Fields]=\"mcpFilterFields\"\n [Values]=\"mcpFilterValues\"\n (ValuesChange)=\"onFilterValuesChange($event)\"\n (Reset)=\"resetPopoverFilters()\">\n </mj-filter-panel>\n </mj-filter-popover>\n\n <!-- Action Buttons based on tab -->\n @switch (ActiveTab) {\n @case ('servers') {\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"createServer()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Server\n </button>\n }\n @case ('connections') {\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"createConnection()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Connection\n </button>\n }\n @case ('tools') {\n <mj-view-toggle\n [Options]=\"toolsViewOptions\"\n [ActiveKey]=\"ToolsViewMode\"\n (KeyChange)=\"setToolsViewMode($any($event))\">\n </mj-view-toggle>\n <button mjButton variant=\"secondary\" size=\"sm\"\n (click)=\"toggleScalableMode(!useScalablePagination)\"\n [title]=\"useScalablePagination ? 'Scale mode on: virtual scroll, paginated' : 'Enable scale mode for large tool sets'\">\n <i class=\"fa-solid\" [class.fa-bolt]=\"useScalablePagination\" [class.fa-bolt-lightning]=\"!useScalablePagination\"></i>\n Scale: {{ useScalablePagination ? 'ON' : 'OFF' }}\n </button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"openTestToolDialog()\">\n <i class=\"fa-solid fa-play\"></i>\n Test Tool\n </button>\n }\n @case ('logs') {\n <mj-refresh-button [Loading]=\"IsLoading\" (Clicked)=\"loadAllData()\"></mj-refresh-button>\n }\n }\n </div>\n <div toolbar>\n <!-- Tab Navigation -->\n <mj-tab-nav\n [Tabs]=\"mcpTabs\"\n [ActiveKey]=\"ActiveTab\"\n (TabChange)=\"setActiveTab($any($event))\">\n </mj-tab-nav>\n <mj-page-search\n Placeholder=\"Search\u2026\"\n [Value]=\"CurrentFilters.searchTerm\"\n (ValueChange)=\"onSearchTermChange($event)\">\n </mj-page-search>\n </div>\n </mj-page-header>\n\n <!-- Main Content -->\n <mj-page-body [Flex]=\"true\">\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 @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 mjButton variant=\"primary\" size=\"sm\" (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\">{{ server.Name }}</span>\n <span class=\"status-badge\" [ngClass]=\"getStatusClass(server.Status)\">\n {{ server.Status }}\n </span>\n </div>\n <div class=\"card-actions\" (click)=\"$event.stopPropagation()\">\n <button mjButton variant=\"flat\" (click)=\"editServer(server)\" title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button mjButton variant=\"flat\" (click)=\"deleteServer(server)\" title=\"Delete\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n </div>\n </div>\n <div class=\"card-body\">\n @if (server.Description) {\n <p class=\"description\">{{ server.Description }}</p>\n }\n <div class=\"details-grid\">\n <div class=\"detail\">\n <span class=\"label\">Transport</span>\n <span class=\"value\">{{ server.TransportType }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Auth</span>\n <span class=\"value\">{{ server.DefaultAuthType }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Connections</span>\n <span class=\"value\">{{ server.ConnectionCount }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Tools</span>\n <span class=\"value\">{{ server.ToolCount }}</span>\n </div>\n @if (server.ServerURL) {\n <div class=\"detail full-width\">\n <span class=\"label\">URL</span>\n <span class=\"value url\">{{ server.ServerURL }}</span>\n </div>\n }\n @if (server.Command) {\n <div class=\"detail full-width\">\n <span class=\"label\">Command</span>\n <span class=\"value command\">{{ server.Command }}</span>\n </div>\n }\n <div class=\"detail\">\n <span class=\"label\">Last Sync</span>\n <span class=\"value\">{{ formatDate(server.LastSyncAt) }}</span>\n </div>\n </div>\n </div>\n <!-- Expanded Tools Section -->\n @if (isServerExpanded(server)) {\n <div class=\"expanded-tools-section\">\n <div class=\"tools-section-header\">\n <i class=\"fa-solid fa-wrench\"></i>\n <span>Available Tools ({{ getToolsForServer(server.ID).length }})</span>\n </div>\n @if (getToolsForServer(server.ID).length === 0) {\n <div class=\"no-tools-message\">\n <i class=\"fa-solid fa-info-circle\"></i>\n No tools discovered yet. Sync a connection to discover tools.\n </div>\n } @else {\n <div class=\"tools-mini-list\">\n @for (tool of getToolsForServer(server.ID); track tool.ID) {\n <div class=\"tool-mini-card\">\n <div class=\"tool-mini-info\">\n <span class=\"tool-mini-name\">{{ tool.ToolTitle || tool.ToolName }}</span>\n <span class=\"tool-mini-params\">\n <i class=\"fa-solid fa-sliders\"></i>\n {{ getParamCount(tool) }} params\n </span>\n </div>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"runToolFromCard(tool); $event.stopPropagation()\"\n title=\"Test this tool\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n }\n\n <!-- Connections Tab -->\n @case ('connections') {\n <div class=\"data-grid connections-grid\">\n @if (filteredConnections.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-link\"></i>\n <p>No connections configured</p>\n <button mjButton variant=\"primary\" size=\"sm\" (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\">{{ conn.Name }}</span>\n <span class=\"status-badge\" [ngClass]=\"getStatusClass(conn.Status)\">\n {{ conn.Status }}\n </span>\n </div>\n <div class=\"card-actions\" (click)=\"$event.stopPropagation()\">\n <button mjButton\n variant=\"flat\"\n (click)=\"syncConnectionTools(conn)\"\n [disabled]=\"isSyncing(conn.ID)\"\n title=\"Sync Tools\">\n @if (isSyncing(conn.ID)) {\n <i class=\"fa-solid fa-sync fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-sync\"></i>\n }\n </button>\n <button mjButton variant=\"flat\" (click)=\"editConnection(conn)\" title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button mjButton variant=\"flat\" (click)=\"deleteConnection(conn)\" title=\"Delete\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n </div>\n </div>\n <!-- Sync Progress Banner -->\n @if (isSyncing(conn.ID)) {\n <div class=\"sync-progress-banner\">\n <i class=\"fa-solid fa-circle-notch fa-spin\"></i>\n <span class=\"sync-message\">{{ getSyncProgressMessage(conn.ID) || 'Syncing...' }}</span>\n </div>\n }\n <!-- Sync Result Banner -->\n @if (getSyncState(conn.ID)?.lastResult && !isSyncing(conn.ID)) {\n <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\">{{ conn.Description }}</p>\n }\n <div class=\"details-grid\">\n <div class=\"detail\">\n <span class=\"label\">Server</span>\n <span class=\"value\">{{ conn.ServerName }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Auto Sync</span>\n <span class=\"value\">{{ conn.AutoSyncTools ? 'Yes' : 'No' }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Logging</span>\n <span class=\"value\">{{ conn.LogToolCalls ? 'Enabled' : 'Disabled' }}</span>\n </div>\n <div class=\"detail\">\n <span class=\"label\">Last Connected</span>\n <span class=\"value\">{{ formatDate(conn.LastConnectedAt) }}</span>\n </div>\n @if (conn.LastErrorMessage) {\n <div class=\"detail full-width error\">\n <span class=\"label\">Last Error</span>\n <span class=\"value\">{{ conn.LastErrorMessage }}</span>\n </div>\n }\n </div>\n </div>\n <!-- Expanded Tools Section -->\n @if (isConnectionExpanded(conn)) {\n <div class=\"expanded-tools-section\">\n <div class=\"tools-section-header\">\n <i class=\"fa-solid fa-wrench\"></i>\n <span>Available Tools ({{ getToolsForConnection(conn.ID).length }})</span>\n </div>\n @if (getToolsForConnection(conn.ID).length === 0) {\n <div class=\"no-tools-message\">\n <i class=\"fa-solid fa-info-circle\"></i>\n No tools discovered yet. Click the sync button to discover tools.\n </div>\n } @else {\n <div class=\"tools-mini-list\">\n @for (tool of getToolsForConnection(conn.ID); track tool.ID) {\n <div class=\"tool-mini-card\">\n <div class=\"tool-mini-info\">\n <span class=\"tool-mini-name\">{{ tool.ToolTitle || tool.ToolName }}</span>\n <span class=\"tool-mini-params\">\n <i class=\"fa-solid fa-sliders\"></i>\n {{ getParamCount(tool) }} params\n </span>\n </div>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"runToolFromCard(tool, conn); $event.stopPropagation()\"\n title=\"Test this tool with this connection\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n }\n\n <!-- Tools Tab -->\n @case ('tools') {\n @if (useScalablePagination) {\n <div style=\"padding:8px 12px;font-size:0.8rem;color:var(--mj-text-secondary,#475569);background:var(--mj-bg-surface-card,#f8f9fa);border-radius:4px;margin-bottom:6px\">\n Showing <strong>{{ visiblePagedTools.length }}</strong> of <strong>{{ scaleDenominator() }}</strong> tools\n </div>\n <div style=\"height:calc(100vh - 260px);width:100%;overflow-y:auto;border:1px solid var(--mj-border-subtle,#eee);border-radius:4px\" (scroll)=\"onToolsScrollNative($event)\">\n @for (tool of visiblePagedTools; track tool.ID) {\n <div style=\"display:flex;align-items:center;gap:12px;padding:12px 14px;border-bottom:1px solid var(--mj-border-subtle,#eee);height:72px;box-sizing:border-box\">\n <i class=\"fa-solid fa-wrench\" style=\"color:var(--mj-brand-primary,#264FAF)\"></i>\n <div style=\"flex:1;min-width:0\">\n <div style=\"font-weight:500;font-size:0.875rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ tool.ToolTitle || tool.ToolName }}</div>\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ tool.ServerName }} \u00B7 {{ tool.ToolName }}</div>\n </div>\n <button type=\"button\" (click)=\"toggleFavorite(tool.ID, $event)\" [title]=\"isFavorited(tool.ID) ? 'Unfavorite' : 'Favorite'\" style=\"background:transparent;border:none;cursor:pointer;padding:4px;font-size:1rem\" [style.color]=\"isFavorited(tool.ID) ? 'var(--mj-status-warning, #f59e0b)' : 'var(--mj-text-muted, #bbb)'\">\n <i [class]=\"isFavorited(tool.ID) ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(tool.Status)\">{{ tool.Status }}</span>\n </div>\n }\n @if (toolsLoading) {\n <div style=\"padding:12px;text-align:center;font-size:0.8rem;color:var(--mj-text-muted,#888)\">Loading more\u2026</div>\n }\n </div>\n } @else if (ServerGroups.length === 0) {\n <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\">{{ getServerToolCount(group.server.ID) }} tools</span>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(group.server.Status)\">\n {{ group.server.Status }}\n </span>\n </div>\n <div class=\"server-actions\" (click)=\"$event.stopPropagation()\">\n <button mjButton variant=\"flat\" (click)=\"openTestToolDialog(undefined, undefined)\" title=\"Test a tool\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n </div>\n </div>\n\n <!-- Tools Content -->\n @if (group.expanded) {\n <!-- Card View -->\n @if (ToolsViewMode === 'card') {\n <div class=\"tools-grid\">\n @for (tool of group.tools; track tool.ID) {\n <div class=\"tool-card\" [class.expanded]=\"isToolExpanded(tool)\">\n <div class=\"tool-card-header\" (click)=\"toggleToolExpand(tool)\">\n <div class=\"tool-title\">\n <i class=\"fa-solid fa-wrench\"></i>\n <span class=\"name\">{{ tool.ToolTitle || tool.ToolName }}</span>\n </div>\n <div class=\"tool-meta\">\n <span class=\"param-badge\" title=\"Parameters\">\n <i class=\"fa-solid fa-sliders\"></i>\n {{ getParamCount(tool) }}\n </span>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(tool.Status)\">\n {{ tool.Status }}\n </span>\n </div>\n </div>\n @if (tool.ToolDescription) {\n <p class=\"tool-description\">{{ tool.ToolDescription }}</p>\n }\n <div class=\"tool-card-actions\">\n <button type=\"button\" (click)=\"toggleFavorite(tool.ID, $event)\" [title]=\"isFavorited(tool.ID) ? 'Unfavorite' : 'Favorite'\" style=\"background:transparent;border:none;cursor:pointer;padding:4px;font-size:1rem\" [style.color]=\"isFavorited(tool.ID) ? 'var(--mj-status-warning, #f59e0b)' : 'var(--mj-text-muted, #bbb)'\">\n <i [class]=\"isFavorited(tool.ID) ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"openTestToolDialog(tool)\" title=\"Test this tool\">\n <i class=\"fa-solid fa-play\"></i>\n Test\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"toggleToolExpand(tool)\" title=\"View details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!isToolExpanded(tool)\" [class.fa-chevron-up]=\"isToolExpanded(tool)\"></i>\n {{ isToolExpanded(tool) ? 'Less' : 'More' }}\n </button>\n </div>\n <!-- Expanded Details -->\n @if (isToolExpanded(tool)) {\n <div class=\"tool-details\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Tool Name:</span>\n <span class=\"detail-value mono\">{{ tool.ToolName }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Parameters:</span>\n <span class=\"detail-value\">\n {{ getParamCount(tool) }} total, {{ getRequiredParamCount(tool) }} required\n </span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Discovered:</span>\n <span class=\"detail-value\">{{ formatDate(tool.DiscoveredAt) }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Last Seen:</span>\n <span class=\"detail-value\">{{ formatDate(tool.LastSeenAt) }}</span>\n </div>\n @if (tool.InputSchema) {\n <div class=\"detail-row full\">\n <span class=\"detail-label\">Input Schema:</span>\n <pre class=\"schema-preview\">{{ getFormattedInputSchema(tool) }}</pre>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n } @else {\n <!-- List View -->\n <div class=\"tools-list\">\n <table>\n <thead>\n <tr>\n <th>Tool</th>\n <th>Description</th>\n <th>Params</th>\n <th>Status</th>\n <th>Last Seen</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (tool of group.tools; track tool.ID) {\n <tr [class.expanded]=\"isToolExpanded(tool)\">\n <td class=\"tool-name-cell\">\n <i class=\"fa-solid fa-wrench\"></i>\n <div class=\"tool-name-info\">\n <span class=\"tool-title\">{{ tool.ToolTitle || tool.ToolName }}</span>\n @if (tool.ToolTitle) {\n <span class=\"tool-code\">{{ tool.ToolName }}</span>\n }\n </div>\n </td>\n <td class=\"description-cell\">{{ tool.ToolDescription || '-' }}</td>\n <td>\n <span class=\"param-count\">{{ getParamCount(tool) }}</span>\n </td>\n <td>\n <span class=\"status-badge small\" [ngClass]=\"getStatusClass(tool.Status)\">\n {{ tool.Status }}\n </span>\n </td>\n <td>{{ formatDate(tool.LastSeenAt) }}</td>\n <td class=\"actions-cell\">\n <button type=\"button\" (click)=\"toggleFavorite(tool.ID, $event)\" [title]=\"isFavorited(tool.ID) ? 'Unfavorite' : 'Favorite'\" style=\"background:transparent;border:none;cursor:pointer;padding:4px;font-size:0.95rem;margin-right:4px\" [style.color]=\"isFavorited(tool.ID) ? 'var(--mj-status-warning, #f59e0b)' : 'var(--mj-text-muted, #bbb)'\">\n <i [class]=\"isFavorited(tool.ID) ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"openTestToolDialog(tool)\" title=\"Test\">\n <i class=\"fa-solid fa-play\"></i>\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"toggleToolExpand(tool)\" title=\"Details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!isToolExpanded(tool)\" [class.fa-chevron-up]=\"isToolExpanded(tool)\"></i>\n </button>\n </td>\n </tr>\n @if (isToolExpanded(tool)) {\n <tr class=\"detail-row-expanded\">\n <td colspan=\"6\">\n <div class=\"inline-details\">\n <div class=\"detail-section\">\n <span class=\"detail-label\">Tool Name:</span>\n <span class=\"detail-value mono\">{{ tool.ToolName }}</span>\n </div>\n <div class=\"detail-section\">\n <span class=\"detail-label\">Discovered:</span>\n <span class=\"detail-value\">{{ formatDate(tool.DiscoveredAt) }}</span>\n </div>\n <div class=\"detail-section\">\n <span class=\"detail-label\">Required Params:</span>\n <span class=\"detail-value\">{{ getRequiredParamCount(tool) }}</span>\n </div>\n @if (tool.InputSchema) {\n <div class=\"detail-section full\">\n <span class=\"detail-label\">Input Schema:</span>\n <pre class=\"schema-preview\">{{ getFormattedInputSchema(tool) }}</pre>\n </div>\n }\n </div>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n }\n }\n </div>\n }\n </div>\n }\n }\n\n <!-- Logs Tab -->\n @case ('logs') {\n <div class=\"data-table\">\n @if (filteredLogs.length === 0) {\n <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 </mj-page-body>\n</mj-page-layout>\n\n<!-- Server Dialog (inline \u2014 avoids sub-component DI issues) -->\n@if (ShowServerDialog) {\n <div style=\"position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\" (click)=\"cancelServerForm()\">\n <div style=\"display:flex;flex-direction:column;background:var(--mj-bg-surface,#fff);border-radius:8px;box-shadow:0 20px 60px rgba(0,0,0,0.3);overflow:hidden;width:600px;max-width:90vw;max-height:90vh\" (click)=\"$event.stopPropagation()\">\n <!-- Title bar -->\n <div style=\"display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <h2 style=\"margin:0;font-size:1.125rem;font-weight:600;color:var(--mj-text-primary,#333)\">{{ EditingServer ? 'Edit MCP Server' : 'Add MCP Server' }}</h2>\n <button style=\"display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:none;cursor:pointer;color:var(--mj-text-muted,#888);border-radius:4px\" (click)=\"cancelServerForm()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Body -->\n <div style=\"flex:1;overflow-y:auto;padding:20px;color:var(--mj-text-primary,#333)\">\n @if (ServerFormError) {\n <div style=\"background:var(--mj-status-error-bg,#fff5f5);border:1px solid var(--mj-status-error-border,#feb2b2);border-radius:6px;padding:10px 14px;margin-bottom:16px;color:var(--mj-status-error-text,#c53030);font-size:0.875rem\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i> {{ ServerFormError }}\n </div>\n }\n <!-- Name -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Name <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #sfName class=\"mj-input\" placeholder=\"e.g., GitHub MCP Server\" [value]=\"ServerForm.Name\" (input)=\"ServerForm.Name = sfName.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n <!-- Description -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Description</label>\n <textarea #sfDesc class=\"mj-textarea\" placeholder=\"Optional description\" [value]=\"ServerForm.Description\" (input)=\"ServerForm.Description = sfDesc.value\" rows=\"3\" style=\"width:100%;box-sizing:border-box\"></textarea>\n </div>\n <!-- Transport Type -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Transport Type <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #sfTransport class=\"mj-input\" [value]=\"ServerForm.TransportType\" (change)=\"ServerForm.TransportType = sfTransport.value\" style=\"width:100%;box-sizing:border-box\">\n @for (t of TransportTypes; track t.value) {\n <option [value]=\"t.value\">{{ t.label }}</option>\n }\n </select>\n </div>\n <!-- Server URL (for HTTP-based transports) -->\n @if (ServerForm.TransportType === 'StreamableHTTP' || ServerForm.TransportType === 'SSE' || ServerForm.TransportType === 'WebSocket') {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Server URL <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #sfUrl class=\"mj-input\" placeholder=\"https://api.example.com/mcp\" [value]=\"ServerForm.ServerURL\" (input)=\"ServerForm.ServerURL = sfUrl.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n }\n <!-- Command (for Stdio) -->\n @if (ServerForm.TransportType === 'Stdio') {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Command <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #sfCmd class=\"mj-input\" placeholder=\"e.g., npx\" [value]=\"ServerForm.Command\" (input)=\"ServerForm.Command = sfCmd.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n }\n <!-- Auth Type -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Default Auth Type</label>\n <select #sfAuth class=\"mj-input\" [value]=\"ServerForm.DefaultAuthType\" (change)=\"ServerForm.DefaultAuthType = sfAuth.value\" style=\"width:100%;box-sizing:border-box\">\n @for (a of AuthTypes; track a.value) {\n <option [value]=\"a.value\">{{ a.label }}</option>\n }\n </select>\n </div>\n <!-- Status -->\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Status</label>\n <select #sfStatus class=\"mj-input\" [value]=\"ServerForm.Status\" (change)=\"ServerForm.Status = sfStatus.value\" style=\"width:100%;box-sizing:border-box\">\n <option value=\"Active\">Active</option>\n <option value=\"Inactive\">Inactive</option>\n </select>\n </div>\n </div>\n <!-- Actions -->\n <div style=\"display:flex;align-items:center;gap:8px;padding:16px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <button mjButton variant=\"primary\" (click)=\"saveServerForm()\" [disabled]=\"ServerFormSaving\">\n @if (ServerFormSaving) { <i class=\"fa-solid fa-spinner fa-spin\"></i> }\n {{ EditingServer ? 'Update' : 'Create' }}\n </button>\n <button mjButton (click)=\"cancelServerForm()\">Cancel</button>\n </div>\n </div>\n </div>\n}\n\n<!-- Connection Dialog (inline) -->\n@if (ShowConnectionDialog) {\n <div style=\"position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\" (click)=\"cancelConnectionForm()\">\n <div style=\"display:flex;flex-direction:column;background:var(--mj-bg-surface,#fff);border-radius:8px;box-shadow:0 20px 60px rgba(0,0,0,0.3);overflow:hidden;width:560px;max-width:90vw;max-height:90vh\" (click)=\"$event.stopPropagation()\">\n <div style=\"display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <h2 style=\"margin:0;font-size:1.125rem;font-weight:600;color:var(--mj-text-primary,#333)\">Add Connection</h2>\n <button style=\"display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:none;cursor:pointer;color:var(--mj-text-muted,#888);border-radius:4px\" (click)=\"cancelConnectionForm()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div style=\"flex:1;overflow-y:auto;padding:20px;color:var(--mj-text-primary,#333)\">\n @if (ConnectionFormError) {\n <div style=\"background:var(--mj-status-error-bg,#fff5f5);border:1px solid var(--mj-status-error-border,#feb2b2);border-radius:6px;padding:10px 14px;margin-bottom:16px;color:var(--mj-status-error-text,#c53030);font-size:0.875rem\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i> {{ ConnectionFormError }}\n </div>\n }\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Server <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #cfServer class=\"mj-input\" [value]=\"ConnectionForm.MCPServerID\" (change)=\"ConnectionForm.MCPServerID = cfServer.value\" style=\"width:100%;box-sizing:border-box\">\n <option value=\"\">\u2014 select server \u2014</option>\n @for (s of servers; track s.ID) {\n <option [value]=\"s.ID\">{{ s.Name }}</option>\n }\n </select>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Name <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #cfName class=\"mj-input\" placeholder=\"e.g., Zapier Connection\" [value]=\"ConnectionForm.Name\" (input)=\"ConnectionForm.Name = cfName.value\" style=\"width:100%;box-sizing:border-box\" />\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Bearer Token</label>\n <input #cfToken class=\"mj-input\" placeholder=\"Paste bearer token here\" [value]=\"ConnectionForm.BearerToken\" (input)=\"ConnectionForm.BearerToken = cfToken.value\" style=\"width:100%;box-sizing:border-box;font-family:monospace;font-size:0.8rem\" />\n <span style=\"font-size:0.75rem;color:var(--mj-text-muted,#888)\">Will be stored as a Credential and linked to this connection</span>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Status</label>\n <select #cfStatus class=\"mj-input\" [value]=\"ConnectionForm.Status\" (change)=\"ConnectionForm.Status = cfStatus.value\" style=\"width:100%;box-sizing:border-box\">\n <option value=\"Active\">Active</option>\n <option value=\"Inactive\">Inactive</option>\n </select>\n </div>\n </div>\n <div style=\"display:flex;align-items:center;gap:8px;padding:16px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <button mjButton variant=\"primary\" (click)=\"saveConnectionForm()\" [disabled]=\"ConnectionFormSaving\">\n @if (ConnectionFormSaving) { <i class=\"fa-solid fa-spinner fa-spin\"></i> }\n Create\n </button>\n <button mjButton (click)=\"cancelConnectionForm()\">Cancel</button>\n </div>\n </div>\n </div>\n}\n\n<!-- Test Tool Dialog (inline) -->\n@if (ShowTestToolDialog) {\n <div style=\"position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\" (click)=\"testCloseDialog()\">\n <div style=\"display:flex;flex-direction:column;background:var(--mj-bg-surface,#fff);border-radius:8px;box-shadow:0 20px 60px rgba(0,0,0,0.3);overflow:hidden;width:640px;max-width:90vw;max-height:90vh\" (click)=\"$event.stopPropagation()\">\n <!-- Title bar -->\n <div style=\"display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <h2 style=\"margin:0;font-size:1.125rem;font-weight:600;color:var(--mj-text-primary,#333)\">\n Test MCP Tool\n @if (TestStep !== 'select') {\n <span style=\"font-size:0.875rem;font-weight:400;color:var(--mj-text-muted,#888);margin-left:8px\">\u2014 {{ TestSelectedTool?.ToolTitle || TestSelectedTool?.ToolName }}</span>\n }\n </h2>\n <button style=\"display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:none;cursor:pointer;color:var(--mj-text-muted,#888);border-radius:4px\" (click)=\"testCloseDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Step indicator -->\n <div style=\"display:flex;gap:0;padding:10px 20px;background:var(--mj-bg-surface-card,#f9f9f9);border-bottom:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n <span style=\"font-size:0.8rem;font-weight:500;\" [style.color]=\"TestStep==='select' ? 'var(--mj-brand-primary,#264FAF)' : 'var(--mj-text-muted,#888)'\">1. Select</span>\n <span style=\"margin:0 8px;color:var(--mj-text-muted,#888)\">\u203A</span>\n <span style=\"font-size:0.8rem;font-weight:500;\" [style.color]=\"TestStep==='configure' ? 'var(--mj-brand-primary,#264FAF)' : 'var(--mj-text-muted,#888)'\">2. Configure</span>\n <span style=\"margin:0 8px;color:var(--mj-text-muted,#888)\">\u203A</span>\n <span style=\"font-size:0.8rem;font-weight:500;\" [style.color]=\"TestStep==='results' ? 'var(--mj-brand-primary,#264FAF)' : 'var(--mj-text-muted,#888)'\">3. Results</span>\n </div>\n <!-- Body -->\n <div style=\"flex:1;overflow-y:auto;padding:20px;color:var(--mj-text-primary,#333)\">\n @if (TestStep === 'select') {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Server <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #tSrv class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (change)=\"onTestServerChange(tSrv.value)\">\n <option value=\"\">\u2014 select server \u2014</option>\n @for (s of servers; track s.ID) {\n <option [value]=\"s.ID\" [selected]=\"TestToolServerID === s.ID\">{{ s.Name }}</option>\n }\n </select>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Connection <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <select #tConn class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" [attr.disabled]=\"!TestToolServerID ? true : null\" (change)=\"onTestConnectionChange(tConn.value)\">\n <option value=\"\">\u2014 select connection \u2014</option>\n @for (c of TestFilteredConnections; track c.ID) {\n <option [value]=\"c.ID\" [selected]=\"TestToolConnectionID === c.ID\">{{ c.Name }}</option>\n }\n </select>\n </div>\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">Tool <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span></label>\n <input #tSearch type=\"text\" class=\"mj-input\" style=\"width:100%;box-sizing:border-box;margin-bottom:6px\" placeholder=\"Search tools\u2026 (Recently used appear first)\" [value]=\"TestToolSearch\" (input)=\"onTestToolSearchChange(tSearch.value)\" [attr.disabled]=\"!TestToolServerID ? true : null\" />\n <div style=\"max-height:220px;overflow-y:auto;border:1px solid var(--mj-border-default,#e0e0e0);border-radius:4px;background:var(--mj-bg-surface,#fff)\">\n @if (TestComboboxTools.length === 0) {\n <div style=\"padding:12px;text-align:center;font-size:0.8rem;color:var(--mj-text-muted,#888)\">No tools match.</div>\n }\n @for (t of TestComboboxTools; track t.ID) {\n <div (click)=\"pickTestTool(t.ID)\" style=\"padding:8px 12px;cursor:pointer;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--mj-border-subtle,#eee)\" [style.background]=\"TestToolID === t.ID ? 'color-mix(in srgb, var(--mj-brand-primary,#264FAF) 12%, var(--mj-bg-surface,#fff))' : null\">\n @if (isRecentTestTool(t.ID)) {\n <span style=\"font-size:0.65rem;background:var(--mj-brand-primary,#264FAF);color:#fff;padding:1px 6px;border-radius:8px;font-weight:600\">Recent</span>\n }\n <div style=\"flex:1;min-width:0\">\n <div style=\"font-size:0.85rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ t.ToolTitle || t.ToolName }}</div>\n @if (t.ToolTitle && t.ToolName !== t.ToolTitle) {\n <div style=\"font-size:0.7rem;color:var(--mj-text-muted,#888);overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">{{ t.ToolName }}</div>\n }\n </div>\n @if (TestToolID === t.ID) {\n <i class=\"fa-solid fa-check\" style=\"color:var(--mj-brand-primary,#264FAF)\"></i>\n }\n </div>\n }\n </div>\n </div>\n } @else if (TestStep === 'configure') {\n @if (TestParamConfigs.length === 0) {\n <div style=\"text-align:center;padding:24px;color:var(--mj-text-muted,#888)\">\n <i class=\"fa-solid fa-check-circle\" style=\"font-size:2rem;margin-bottom:8px;display:block\"></i>\n <p>This tool requires no parameters.</p>\n </div>\n } @else {\n @for (p of TestParamConfigs; track p.name) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;color:var(--mj-text-primary,#333);margin-bottom:4px\">\n {{ p.name }} @if (p.required) { <span style=\"color:var(--mj-status-error,#e53e3e)\">*</span> }\n </label>\n @if (p.description) {\n <p style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin:0 0 4px 0\">{{ p.description }}</p>\n }\n @if (p.enumValues.length > 0) {\n <select #enumEl class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (change)=\"testSetParam(p.name, enumEl.value)\">\n <option value=\"\">\u2014 select \u2014</option>\n @for (v of p.enumValues; track $index) { <option [value]=\"$any(v)\">{{ $any(v) }}</option> }\n </select>\n } @else if (p.type === 'boolean') {\n <select #boolEl class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (change)=\"testSetParam(p.name, boolEl.value)\">\n <option value=\"false\">false</option>\n <option value=\"true\">true</option>\n </select>\n } @else if (p.type === 'array' || p.type === 'object') {\n <textarea #jsonEl class=\"mj-textarea\" rows=\"3\" style=\"width:100%;box-sizing:border-box;font-family:monospace;font-size:0.8rem\" (input)=\"testSetParam(p.name, jsonEl.value)\" [placeholder]=\"'Enter JSON ' + p.type + '...'\"></textarea>\n } @else {\n <input #strEl class=\"mj-input\" style=\"width:100%;box-sizing:border-box\" (input)=\"testSetParam(p.name, strEl.value)\" [placeholder]=\"p.description || 'Enter value...'\" />\n }\n </div>\n }\n }\n } @else {\n @if (TestExecutionResult?.Success) {\n <div style=\"background:var(--mj-status-success-bg,#f0fff4);border:1px solid var(--mj-status-success-border,#9ae6b4);border-radius:6px;padding:12px 16px;margin-bottom:16px;color:var(--mj-status-success-text,#276749)\">\n <i class=\"fa-solid fa-check-circle\"></i> Execution Successful\n @if (TestExecutionResult?.DurationMs) { <span style=\"float:right;font-size:0.75rem\">{{ TestExecutionResult?.DurationMs }}ms</span> }\n </div>\n } @else {\n <div style=\"background:var(--mj-status-error-bg,#fff5f5);border:1px solid var(--mj-status-error-border,#feb2b2);border-radius:6px;padding:12px 16px;margin-bottom:16px;color:var(--mj-status-error-text,#c53030)\">\n <i class=\"fa-solid fa-times-circle\"></i> Execution Failed\n </div>\n }\n @if (TestExecutionResult?.ErrorMessage) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:4px\">Error</label>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.8rem;white-space:pre-wrap;word-break:break-all;margin:0\">{{ TestExecutionResult?.ErrorMessage }}</pre>\n </div>\n }\n @if (TestExecutionResult?.Result) {\n <div>\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:4px\">Result</label>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.8rem;white-space:pre-wrap;word-break:break-all;overflow-y:auto;max-height:300px;margin:0\">{{ formatTestResult(TestExecutionResult?.Result) }}</pre>\n </div>\n }\n }\n </div>\n <!-- Actions -->\n <div style=\"display:flex;align-items:center;gap:8px;padding:16px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);flex-shrink:0\">\n @if (TestStep === 'select') {\n <button mjButton variant=\"primary\" (click)=\"testProceedToConfig()\" [disabled]=\"!TestCanProceed\">Next <i class=\"fa-solid fa-arrow-right\"></i></button>\n <button mjButton (click)=\"testCloseDialog()\">Cancel</button>\n } @else if (TestStep === 'configure') {\n <button mjButton variant=\"primary\" (click)=\"testExecuteTool()\" [disabled]=\"!TestIsValid || TestIsExecuting\">\n @if (TestIsExecuting) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Executing... } @else { <i class=\"fa-solid fa-play\"></i> Execute }\n </button>\n <button mjButton (click)=\"testGoBack()\"><i class=\"fa-solid fa-arrow-left\"></i> Back</button>\n } @else {\n <button mjButton variant=\"primary\" (click)=\"testRunAgain()\"><i class=\"fa-solid fa-redo\"></i> Run Again</button>\n <button mjButton (click)=\"testGoBack()\">Edit Params</button>\n <button mjButton (click)=\"testCloseDialog()\">Close</button>\n }\n </div>\n </div>\n </div>\n}\n\n<!-- Log Detail Panel (inlined) -->\n@if (ShowLogDetailPanel && SelectedLog) {\n <div style=\"position:fixed;inset:0;z-index:10000;pointer-events:none\">\n <div style=\"position:absolute;inset:0;background:rgba(0,0,0,0.35);pointer-events:auto\" (click)=\"onLogDetailClose()\"></div>\n <div style=\"position:absolute;top:0;right:0;height:100%;width:560px;max-width:95vw;background:var(--mj-bg-surface,#fff);box-shadow:-4px 0 20px rgba(0,0,0,0.15);display:flex;flex-direction:column;pointer-events:auto\">\n <div style=\"padding:16px 20px;border-bottom:1px solid var(--mj-border-default,#e0e0e0);display:flex;align-items:center;justify-content:space-between\">\n <div>\n <h3 style=\"margin:0;font-size:1rem;font-weight:600\">{{ SelectedLog.ToolName }}</h3>\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin-top:4px\">\n <span [class]=\"SelectedLog.Status === 'Success' ? 'status-success' : SelectedLog.Status === 'Error' ? 'status-error' : 'status-running'\" style=\"font-weight:600\">{{ SelectedLog.Status }}</span>\n <span style=\"margin:0 8px\">\u00B7</span>\n <span>{{ formatDate(SelectedLog.StartedAt) }}</span>\n @if (SelectedLog.DurationMs != null) {\n <span style=\"margin:0 8px\">\u00B7</span>\n <span>{{ formatDuration(SelectedLog.DurationMs) }}</span>\n }\n </div>\n </div>\n <button mjButton (click)=\"onLogDetailClose()\" aria-label=\"Close\"><i class=\"fa-solid fa-xmark\"></i></button>\n </div>\n <div style=\"flex:1;overflow-y:auto;padding:16px 20px\">\n <div style=\"margin-bottom:16px\">\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin-bottom:4px\">Connection</div>\n <div style=\"font-size:0.875rem\">{{ SelectedLog.ConnectionName || SelectedLog.ConnectionID }}</div>\n @if (SelectedLog.ServerName) {\n <div style=\"font-size:0.75rem;color:var(--mj-text-muted,#888);margin-top:2px\">{{ SelectedLog.ServerName }}</div>\n }\n </div>\n @if (SelectedLog.InputArgs) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:6px\">Input Arguments</label>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;overflow-y:auto;max-height:240px;margin:0\">{{ formatLogJson(SelectedLog.InputArgs) }}</pre>\n </div>\n }\n @if (SelectedLog.Result) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:6px\">Result</label>\n <pre style=\"background:var(--mj-bg-surface-sunken,#f0f0f0);border-radius:4px;padding:12px;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;overflow-y:auto;max-height:360px;margin:0\">{{ formatLogJson(SelectedLog.Result) }}</pre>\n </div>\n }\n @if (SelectedLog.ErrorMessage) {\n <div style=\"margin-bottom:16px\">\n <label style=\"display:block;font-size:0.875rem;font-weight:500;margin-bottom:6px;color:var(--mj-status-error-text,#c53030)\">Error</label>\n <pre style=\"background:var(--mj-status-error-bg,#fff5f5);border:1px solid var(--mj-status-error-border,#feb2b2);border-radius:4px;padding:12px;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;margin:0;color:var(--mj-status-error-text,#c53030)\">{{ SelectedLog.ErrorMessage }}</pre>\n </div>\n }\n </div>\n <div style=\"padding:12px 20px;border-top:1px solid var(--mj-border-default,#e0e0e0);display:flex;gap:8px;justify-content:flex-start\">\n @if (SelectedLog.ToolID) {\n <button mjButton variant=\"primary\" (click)=\"onRunAgainFromLog({ toolId: SelectedLog.ToolID!, connectionId: SelectedLog.ConnectionID })\"><i class=\"fa-solid fa-redo\"></i> Run Again</button>\n }\n <button mjButton (click)=\"onLogDetailClose()\">Close</button>\n </div>\n </div>\n </div>\n}\n", styles: ["/* MCP Dashboard - Header + Filter Panel Layout (outer shell now provided by <mj-page-layout>) */\n\n/* Tab navigation now provided by <mj-tab-nav>. */\n\n/* ========================================\n Main Content Area\n ======================================== */\n/* Error Banner */\n.error-banner {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n margin: 0 0 16px 0;\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 now provided by <mj-view-toggle>.\n ======================================== */\n\n/* ========================================\n Tools Tab - Server Groups\n ======================================== */\n.tools-container {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.server-group {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n\n.server-group-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.server-group-header:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.server-group.collapsed .server-group-header {\n border-bottom: none;\n}\n\n.server-info {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.expand-icon {\n font-size: 12px;\n color: var(--mj-text-disabled);\n transition: transform 0.2s ease;\n}\n\n.expand-icon.expanded {\n transform: rotate(90deg);\n}\n\n.server-name {\n font-weight: 600;\n font-size: 15px;\n color: var(--mj-text-primary);\n}\n\n.tool-count {\n font-size: 12px;\n color: var(--mj-text-secondary);\n background: rgba(0, 0, 0, 0.05);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.status-badge.small {\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.server-actions {\n display: flex;\n gap: 4px;\n}\n\n/* ========================================\n Tools Grid (Card View)\n ======================================== */\n.tools-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 12px;\n padding: 16px;\n}\n\n.tool-card {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n transition: all 0.2s ease;\n overflow: hidden;\n}\n\n.tool-card:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.tool-card.expanded {\n border-color: var(--mj-brand-primary);\n}\n\n.tool-card-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n padding: 12px 14px;\n cursor: pointer;\n}\n\n.tool-title {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n}\n\n.tool-title i {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n.tool-title .name {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.tool-meta {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.param-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: var(--mj-bg-surface-card);\n border-radius: 10px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.param-badge i {\n font-size: 10px;\n}\n\n.tool-description {\n margin: 0;\n padding: 0 14px 8px 14px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.tool-card-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 8px 14px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n/* Tool Details (Expanded) */\n.tool-details {\n padding: 12px 14px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n}\n\n.tool-details .detail-row {\n display: flex;\n padding: 6px 0;\n font-size: 12px;\n}\n\n.tool-details .detail-row.full {\n flex-direction: column;\n gap: 6px;\n}\n\n.tool-details .detail-label {\n min-width: 100px;\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.tool-details .detail-value {\n color: var(--mj-text-primary);\n}\n\n.tool-details .detail-value.mono {\n font-family: 'Consolas', 'Monaco', monospace;\n background: var(--mj-bg-surface);\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid var(--mj-border-default);\n}\n\n.schema-preview {\n margin: 0;\n padding: 10px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-family: 'Consolas', 'Monaco', monospace;\n font-size: 11px;\n line-height: 1.4;\n max-height: 200px;\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ========================================\n Tools List View\n ======================================== */\n.tools-list {\n overflow: auto;\n}\n\n.tools-list table {\n width: 100%;\n border-collapse: collapse;\n}\n\n.tools-list th,\n.tools-list td {\n padding: 10px 14px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.tools-list th {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n font-weight: 600;\n background: var(--mj-bg-surface-card);\n position: sticky;\n top: 0;\n}\n\n.tools-list tbody tr {\n transition: background 0.15s ease;\n}\n\n.tools-list tbody tr:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.tools-list tbody tr.expanded {\n background: color-mix(in srgb, var(--mj-brand-primary) 4%, transparent);\n}\n\n.tool-name-cell {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.tool-name-cell i {\n color: var(--mj-brand-primary);\n margin-top: 2px;\n}\n\n.tool-name-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.tool-name-info .tool-title {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.tool-name-info .tool-code {\n font-size: 11px;\n font-family: monospace;\n color: var(--mj-text-secondary);\n}\n\n.description-cell {\n max-width: 300px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.param-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n background: var(--mj-bg-surface-card);\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.actions-cell {\n white-space: nowrap;\n}\n\n/* Expanded Detail Row in List View */\n.detail-row-expanded td {\n padding: 0;\n background: var(--mj-bg-surface-card);\n}\n\n.inline-details {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n padding: 14px;\n border-top: 1px dashed var(--mj-border-default);\n}\n\n.inline-details .detail-section {\n display: flex;\n gap: 8px;\n font-size: 12px;\n}\n\n.inline-details .detail-section.full {\n flex-basis: 100%;\n flex-direction: column;\n gap: 6px;\n}\n\n.inline-details .detail-label {\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.inline-details .detail-value {\n color: var(--mj-text-primary);\n}\n\n.inline-details .detail-value.mono {\n font-family: monospace;\n}\n\n/* ========================================\n Responsive Styles\n ======================================== */\n@media (max-width: 900px) {\n .mcp-dashboard {\n flex-direction: column;\n }\n\n .sidebar {\n width: 100%;\n min-width: 100%;\n flex-direction: row;\n height: auto;\n border-right: none;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .sidebar-header {\n padding: 12px 16px;\n border-bottom: none;\n border-right: 1px solid var(--mj-border-default);\n }\n\n .sidebar-nav {\n flex-direction: row;\n padding: 8px;\n overflow-x: auto;\n flex: 1;\n }\n\n .nav-item {\n padding: 8px 12px;\n margin-bottom: 0;\n margin-right: 4px;\n white-space: nowrap;\n }\n\n .sidebar-stats {\n display: none;\n }\n\n .sidebar-footer {\n border-top: none;\n border-left: 1px solid var(--mj-border-default);\n padding: 8px;\n }\n\n .data-grid {\n grid-template-columns: 1fr;\n }\n\n .header-actions {\n flex-wrap: wrap;\n }\n\n .search-box {\n min-width: 100%;\n order: 1;\n }\n}\n\n@media (max-width: 600px) {\n .content-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n\n .header-actions {\n width: 100%;\n }\n\n .search-box {\n width: 100%;\n }\n}\n\n/* ========================================\n Clickable Log Rows\n ======================================== */\n.clickable-row {\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.clickable-row:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent) !important;\n}\n\n.clickable-row .action-cell {\n color: var(--mj-border-strong);\n transition: color 0.15s ease;\n}\n\n.clickable-row:hover .action-cell {\n color: var(--mj-brand-primary);\n}\n\n.action-cell {\n width: 40px;\n text-align: center;\n}\n\n/* ========================================\n Expandable Card Styles\n ======================================== */\n.card-header.clickable {\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.card-header.clickable:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.expand-arrow {\n font-size: 12px;\n color: var(--mj-text-disabled);\n transition: transform 0.2s ease;\n margin-right: 4px;\n}\n\n.expand-arrow.rotated {\n transform: rotate(90deg);\n}\n\n.data-card.expanded {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n/* ========================================\n Expanded Tools Section (Server/Connection Cards)\n ======================================== */\n.expanded-tools-section {\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.tools-section-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.tools-section-header i {\n font-size: 14px;\n}\n\n.no-tools-message {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.no-tools-message i {\n color: var(--mj-text-disabled);\n}\n\n/* Tools Mini List */\n.tools-mini-list {\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: 300px;\n overflow-y: auto;\n}\n\n.tool-mini-card {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n transition: all 0.15s ease;\n}\n\n.tool-mini-card:hover {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 2%, transparent);\n}\n\n.tool-mini-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n}\n\n.tool-mini-name {\n font-weight: 500;\n font-size: 13px;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tool-mini-params {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.tool-mini-params i {\n font-size: 10px;\n}\n\n.tool-mini-card button {\n flex-shrink: 0;\n margin-left: 8px;\n}\n\n/* Search Highlight */\n:host ::ng-deep .search-highlight,\n.search-highlight {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: inherit;\n}\n"] }]
4355
4355
  }], () => [{ type: i0.ChangeDetectorRef }, { type: i1.MCPToolsService }], null); })();
4356
4356
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MCPDashboardComponent, { className: "MCPDashboardComponent", filePath: "src/MCP/mcp-dashboard.component.ts", lineNumber: 230 }); })();
4357
4357
  //# sourceMappingURL=mcp-dashboard.component.js.map