imcp 0.0.14 → 0.0.16

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 (136) hide show
  1. package/dist/core/ConfigurationProvider.d.ts +1 -0
  2. package/dist/core/ConfigurationProvider.js +15 -0
  3. package/dist/core/InstallationService.js +2 -7
  4. package/dist/core/MCPManager.d.ts +11 -2
  5. package/dist/core/MCPManager.js +24 -1
  6. package/dist/core/RequirementService.js +2 -8
  7. package/dist/core/installers/clients/BaseClientInstaller.d.ts +51 -0
  8. package/dist/core/installers/clients/BaseClientInstaller.js +160 -0
  9. package/dist/core/installers/clients/ClientInstaller.d.ts +16 -9
  10. package/dist/core/installers/clients/ClientInstaller.js +80 -527
  11. package/dist/core/installers/clients/ClientInstallerFactory.d.ts +20 -0
  12. package/dist/core/installers/clients/ClientInstallerFactory.js +37 -0
  13. package/dist/core/installers/clients/ClineInstaller.d.ts +18 -0
  14. package/dist/core/installers/clients/ClineInstaller.js +124 -0
  15. package/dist/core/installers/clients/GithubCopilotInstaller.d.ts +34 -0
  16. package/dist/core/installers/clients/GithubCopilotInstaller.js +162 -0
  17. package/dist/core/installers/clients/MSRooCodeInstaller.d.ts +15 -0
  18. package/dist/core/installers/clients/MSRooCodeInstaller.js +122 -0
  19. package/dist/core/installers/requirements/BaseInstaller.d.ts +11 -34
  20. package/dist/core/installers/requirements/BaseInstaller.js +5 -116
  21. package/dist/core/installers/requirements/CommandInstaller.d.ts +6 -1
  22. package/dist/core/installers/requirements/CommandInstaller.js +7 -0
  23. package/dist/core/installers/requirements/GeneralInstaller.d.ts +6 -1
  24. package/dist/core/installers/requirements/GeneralInstaller.js +9 -4
  25. package/dist/core/installers/requirements/NpmInstaller.d.ts +46 -7
  26. package/dist/core/installers/requirements/NpmInstaller.js +150 -58
  27. package/dist/core/installers/requirements/PipInstaller.d.ts +9 -0
  28. package/dist/core/installers/requirements/PipInstaller.js +66 -28
  29. package/dist/core/onboard/FeedOnboardService.d.ts +50 -13
  30. package/dist/core/onboard/FeedOnboardService.js +263 -88
  31. package/dist/core/onboard/OnboardProcessor.d.ts +79 -0
  32. package/dist/core/onboard/OnboardProcessor.js +290 -0
  33. package/dist/core/onboard/OnboardStatus.d.ts +49 -0
  34. package/dist/core/onboard/OnboardStatus.js +10 -0
  35. package/dist/core/onboard/OnboardStatusManager.d.ts +57 -0
  36. package/dist/core/onboard/OnboardStatusManager.js +176 -0
  37. package/dist/core/types.d.ts +4 -5
  38. package/dist/core/validators/FeedValidator.d.ts +8 -1
  39. package/dist/core/validators/FeedValidator.js +60 -7
  40. package/dist/core/validators/IServerValidator.d.ts +19 -0
  41. package/dist/core/validators/IServerValidator.js +2 -0
  42. package/dist/core/validators/SSEServerValidator.d.ts +15 -0
  43. package/dist/core/validators/SSEServerValidator.js +39 -0
  44. package/dist/core/validators/ServerValidatorFactory.d.ts +24 -0
  45. package/dist/core/validators/ServerValidatorFactory.js +45 -0
  46. package/dist/core/validators/StdioServerValidator.d.ts +46 -0
  47. package/dist/core/validators/StdioServerValidator.js +229 -0
  48. package/dist/services/InstallRequestValidator.d.ts +1 -1
  49. package/dist/services/ServerService.d.ts +9 -6
  50. package/dist/services/ServerService.js +18 -7
  51. package/dist/utils/adoUtils.d.ts +29 -0
  52. package/dist/utils/adoUtils.js +252 -0
  53. package/dist/utils/clientUtils.d.ts +0 -7
  54. package/dist/utils/clientUtils.js +0 -42
  55. package/dist/utils/githubUtils.d.ts +10 -0
  56. package/dist/utils/githubUtils.js +22 -0
  57. package/dist/utils/macroExpressionUtils.d.ts +38 -0
  58. package/dist/utils/macroExpressionUtils.js +116 -0
  59. package/dist/utils/osUtils.d.ts +4 -20
  60. package/dist/utils/osUtils.js +78 -23
  61. package/dist/web/contract/serverContract.d.ts +3 -1
  62. package/dist/web/public/css/notifications.css +48 -17
  63. package/dist/web/public/css/onboard.css +66 -3
  64. package/dist/web/public/index.html +84 -16
  65. package/dist/web/public/js/api.js +3 -6
  66. package/dist/web/public/js/flights/flights.js +127 -0
  67. package/dist/web/public/js/modal/installation.js +5 -5
  68. package/dist/web/public/js/modal/modalSetup.js +3 -2
  69. package/dist/web/public/js/notifications.js +66 -27
  70. package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
  71. package/dist/web/public/js/onboard/formProcessor.js +810 -255
  72. package/dist/web/public/js/onboard/index.js +328 -85
  73. package/dist/web/public/js/onboard/publishHandler.js +132 -0
  74. package/dist/web/public/js/onboard/state.js +61 -17
  75. package/dist/web/public/js/onboard/templates.js +217 -249
  76. package/dist/web/public/js/onboard/uiHandlers.js +679 -117
  77. package/dist/web/public/js/onboard/validationHandlers.js +378 -0
  78. package/dist/web/public/js/serverCategoryList.js +15 -2
  79. package/dist/web/public/onboard.html +191 -45
  80. package/dist/web/public/styles.css +91 -1
  81. package/dist/web/server.d.ts +0 -10
  82. package/dist/web/server.js +131 -22
  83. package/package.json +2 -2
  84. package/src/core/ConfigurationProvider.ts +15 -0
  85. package/src/core/InstallationService.ts +2 -7
  86. package/src/core/MCPManager.ts +26 -1
  87. package/src/core/RequirementService.ts +2 -9
  88. package/src/core/installers/clients/BaseClientInstaller.ts +196 -0
  89. package/src/core/installers/clients/ClientInstaller.ts +97 -608
  90. package/src/core/installers/clients/ClientInstallerFactory.ts +43 -0
  91. package/src/core/installers/clients/ClineInstaller.ts +135 -0
  92. package/src/core/installers/clients/GithubCopilotInstaller.ts +179 -0
  93. package/src/core/installers/clients/MSRooCodeInstaller.ts +133 -0
  94. package/src/core/installers/requirements/BaseInstaller.ts +13 -136
  95. package/src/core/installers/requirements/CommandInstaller.ts +9 -1
  96. package/src/core/installers/requirements/GeneralInstaller.ts +11 -4
  97. package/src/core/installers/requirements/NpmInstaller.ts +178 -61
  98. package/src/core/installers/requirements/PipInstaller.ts +68 -29
  99. package/src/core/onboard/FeedOnboardService.ts +346 -0
  100. package/src/core/onboard/OnboardProcessor.ts +305 -0
  101. package/src/core/onboard/OnboardStatus.ts +55 -0
  102. package/src/core/onboard/OnboardStatusManager.ts +188 -0
  103. package/src/core/types.ts +4 -5
  104. package/src/core/validators/FeedValidator.ts +79 -0
  105. package/src/core/validators/IServerValidator.ts +21 -0
  106. package/src/core/validators/SSEServerValidator.ts +43 -0
  107. package/src/core/validators/ServerValidatorFactory.ts +51 -0
  108. package/src/core/validators/StdioServerValidator.ts +259 -0
  109. package/src/services/InstallRequestValidator.ts +1 -1
  110. package/src/services/ServerService.ts +22 -7
  111. package/src/utils/adoUtils.ts +291 -0
  112. package/src/utils/clientUtils.ts +0 -44
  113. package/src/utils/githubUtils.ts +24 -0
  114. package/src/utils/macroExpressionUtils.ts +121 -0
  115. package/src/utils/osUtils.ts +89 -24
  116. package/src/web/contract/serverContract.ts +74 -0
  117. package/src/web/public/css/notifications.css +48 -17
  118. package/src/web/public/css/onboard.css +107 -0
  119. package/src/web/public/index.html +84 -16
  120. package/src/web/public/js/api.js +3 -6
  121. package/src/web/public/js/flights/flights.js +127 -0
  122. package/src/web/public/js/modal/installation.js +5 -5
  123. package/src/web/public/js/modal/modalSetup.js +3 -2
  124. package/src/web/public/js/notifications.js +66 -27
  125. package/src/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
  126. package/src/web/public/js/onboard/formProcessor.js +864 -0
  127. package/src/web/public/js/onboard/index.js +374 -0
  128. package/src/web/public/js/onboard/publishHandler.js +132 -0
  129. package/src/web/public/js/onboard/state.js +76 -0
  130. package/src/web/public/js/onboard/templates.js +343 -0
  131. package/src/web/public/js/onboard/uiHandlers.js +758 -0
  132. package/src/web/public/js/onboard/validationHandlers.js +378 -0
  133. package/src/web/public/js/serverCategoryList.js +15 -2
  134. package/src/web/public/onboard.html +296 -0
  135. package/src/web/public/styles.css +91 -1
  136. package/src/web/server.ts +167 -58
@@ -23,18 +23,42 @@
23
23
 
24
24
  <body class="bg-gray-50 min-h-screen">
25
25
  <div class="container mx-auto px-4 py-6">
26
- <header class="mb-8">
27
- <h1 class="text-3xl font-bold text-gray-900 flex items-center justify-center">
26
+ <div class="flex items-center justify-between mb-8">
27
+ <h1 class="text-3xl font-bold text-gray-900 flex items-center">
28
28
  <i class='bx bx-server mr-3 text-blue-600'></i>
29
29
  IMCP Server Manager
30
30
  </h1>
31
- </header>
32
-
33
- <div class="search-controls mb-6">
34
- <div class="relative max-w-2xl mx-auto">
35
- <input type="text" id="searchBox" placeholder="Search servers or tools..." class="search-input">
36
- <i class='bx bx-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400'></i>
31
+ <div class="lg:w-2/3 flex justify-end items-center gap-4">
32
+ <div class="relative w-48 focus-within:w-96 transition-all duration-300 ease-in-out">
33
+ <input type="text" id="searchBox"
34
+ placeholder="Search..."
35
+ data-expanded-placeholder="Search servers or tools..."
36
+ class="w-full px-10 py-2.5 bg-gray-50 border border-gray-200 rounded-lg
37
+ text-gray-700 placeholder-gray-400
38
+ focus:border-blue-400 focus:ring-2 focus:ring-blue-100
39
+ transition-all duration-300 ease-in-out">
40
+ <i class='bx bx-search absolute left-3.5 top-1/2 transform -translate-y-1/2 text-gray-400'></i>
41
+ </div>
42
+ <button id="onboardButton" style="display: none;"
43
+ class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center">
44
+ <i class='bx bx-plus-circle mr-2'></i>
45
+ Onboard
46
+ </button>
37
47
  </div>
48
+ <script>
49
+ document.addEventListener('DOMContentLoaded', () => {
50
+ const searchBox = document.getElementById('searchBox');
51
+ const expandedPlaceholder = searchBox.dataset.expandedPlaceholder;
52
+ searchBox.addEventListener('focus', () => {
53
+ setTimeout(() => searchBox.placeholder = expandedPlaceholder, 150);
54
+ });
55
+ searchBox.addEventListener('blur', () => {
56
+ if (!searchBox.value) {
57
+ searchBox.placeholder = 'Search...';
58
+ }
59
+ });
60
+ });
61
+ </script>
38
62
  </div>
39
63
 
40
64
  <div class="flex flex-col lg:flex-row gap-6">
@@ -147,10 +171,15 @@
147
171
  import { setupSearch } from './js/serverCategoryList.js';
148
172
  import { showServerDetails } from './js/serverCategoryDetails.js';
149
173
  import { showInstallModal, closeModal, setupModalOutsideClick } from './js/modal.js';
174
+ // import { showOnboardModal as navigateToOnboard } from './js/onboard/index.js'; // We are directly navigating
175
+ import flights, { getFlightQueryParameters, buildUrlWithFlights } from './js/flights/flights.js';
150
176
 
151
177
  // Make necessary functions available globally
152
178
  window.showServerDetails = showServerDetails;
153
179
  window.showInstallModal = showInstallModal;
180
+ // window.showOnboardModal = showOnboardModal; // This might conflict if showOnboardModal is also in onboard/index.js
181
+ // We'll rely on the event listener above for the main page's onboard button.
182
+ // If other parts of the app call window.showOnboardModal, they'll need updating too.
154
183
  window.closeModal = closeModal;
155
184
  window.handleInstallServer = handleInstallServer;
156
185
  window.installRequirement = installRequirement;
@@ -162,23 +191,62 @@
162
191
  setupSearch();
163
192
  setupModalOutsideClick();
164
193
 
194
+ const onboardButton = document.getElementById('onboardButton');
195
+ if (flights.enableOnboard) {
196
+ onboardButton.style.display = 'flex'; // Make it visible
197
+ onboardButton.addEventListener('click', () => {
198
+ // The original showOnboardModal in onboard/index.js likely does window.location.href = 'onboard.html';
199
+ // We need to preserve existing flight parameters.
200
+ const flightQueryString = getFlightQueryParameters();
201
+ // Assuming navigateToOnboard from onboard/index.js handles the actual navigation
202
+ // If it directly sets window.location.href, we might need to adjust how it's called or modify it.
203
+ // For now, let's assume we can adapt its usage or it's a simple navigation.
204
+ // The simplest way if showOnboardModal in onboard/index.js is just `window.location.href = 'onboard.html'`
205
+ // would be to directly navigate here.
206
+ window.location.href = buildUrlWithFlights('onboard.html');
207
+ });
208
+ }
209
+ // No 'else' block needed as it's hidden by default via inline style
210
+
165
211
  try {
166
212
  // First fetch categories and wait for completion
167
- await fetchServerCategories();
213
+ // fetchServerCategories will call renderServerCategoryList, which might call loadLastSelectedCategory.
214
+ // loadLastSelectedCategory calls showServerDetails.
215
+ // The logic for setting initial category URL is now more centralized.
216
+
217
+ await fetchServerCategories(); // This populates allServerCategoriesData
168
218
 
169
- // Check URL parameters for category
170
219
  const urlParams = new URLSearchParams(window.location.search);
171
220
  const categoryParam = urlParams.get('category');
221
+ const lastSelectedCategoryFromStorage = localStorage.getItem('lastSelectedCategory');
172
222
 
173
- // If we have a category parameter or last selected category
174
- const lastSelected = categoryParam || localStorage.getItem('lastSelectedCategory');
175
-
176
- // Only show details after data is loaded
177
- if (lastSelected) {
178
- await showServerDetails(lastSelected);
223
+ if (categoryParam) {
224
+ // Category is in URL, load it
225
+ await window.showServerDetails(categoryParam);
226
+ } else if (lastSelectedCategoryFromStorage) {
227
+ // Category not in URL, but in localStorage. Redirect to URL with this category and flights.
228
+ window.location.href = buildUrlWithFlights('index.html', { category: lastSelectedCategoryFromStorage });
229
+ return; // Stop further execution, page will reload
230
+ } else if (allServerCategoriesData && allServerCategoriesData.length > 0) {
231
+ // No category in URL or localStorage, pick first from list and redirect.
232
+ window.location.href = buildUrlWithFlights('index.html', { category: allServerCategoriesData[0].name });
233
+ return; // Stop further execution, page will reload
234
+ } else {
235
+ // No categories at all, or data still loading. Default placeholder will be shown.
236
+ // Or, if allServerCategoriesData is empty after fetch, the placeholder from HTML remains.
179
237
  }
238
+
180
239
  } catch (error) {
181
240
  console.error('Error during initialization:', error);
241
+ // Potentially show a user-friendly error message in the UI
242
+ const serverCategoryDetailsDiv = document.getElementById('serverCategoryDetails');
243
+ if (serverCategoryDetailsDiv) {
244
+ serverCategoryDetailsDiv.innerHTML = `<p class="text-red-500">Error initializing page: ${error.message}</p>`;
245
+ }
246
+ const serverCategoryListDiv = document.getElementById('serverCategoryList');
247
+ if (serverCategoryListDiv) {
248
+ serverCategoryListDiv.innerHTML = `<p class="text-red-500">Could not load categories.</p>`;
249
+ }
182
250
  }
183
251
  });
184
252
  </script>
@@ -3,6 +3,8 @@ import { showToast } from './notifications.js';
3
3
 
4
4
  // Global store for server data
5
5
  let allServerCategoriesData = [];
6
+ // Make it available globally for legacy code
7
+ window.allServerCategoriesData = allServerCategoriesData;
6
8
 
7
9
  // Fetch and display servers
8
10
  async function fetchServerCategories() {
@@ -14,16 +16,11 @@ async function fetchServerCategories() {
14
16
  if (!responseData.success || !responseData.data) {
15
17
  throw new Error(`API Error fetching servers: ${responseData.error || 'Invalid data format'}`);
16
18
  }
17
- allServerCategoriesData = responseData.data; // Store globally
19
+ allServerCategoriesData = window.allServerCategoriesData = responseData.data; // Update both local and global references
18
20
 
19
21
  // Use the renderServerCategoryList function to display servers
20
22
  renderServerCategoryList(allServerCategoriesData);
21
23
 
22
- // Show the first category by default if there are categories
23
- if (allServerCategoriesData.length > 0) {
24
- window.showServerDetails(allServerCategoriesData[0].name);
25
- }
26
-
27
24
  // Check if search is active
28
25
  const searchTerm = document.getElementById('searchBox').value.toLowerCase();
29
26
  if (searchTerm) {
@@ -0,0 +1,127 @@
1
+ // Centralized flight control configuration
2
+
3
+ const FLIGHT_STORAGE_KEY = 'activeFlightSettings';
4
+
5
+ // Default flight settings
6
+ const defaultFlights = {
7
+ enableOnboard: false, // Default: Onboarding is enabled. Set to false to disable.
8
+ // Add other default flights here, e.g., newFeatureX: false,
9
+ };
10
+
11
+ let effectiveFlightSettings = {};
12
+
13
+ // Initialize flight settings
14
+ function initializeFlightSettings() {
15
+ const urlParams = new URLSearchParams(window.location.search);
16
+ let settingsToStore = {};
17
+ let sourcedFromUrl = false;
18
+
19
+ // Check URL parameters first
20
+ for (const key in defaultFlights) {
21
+ if (urlParams.has(key)) {
22
+ const paramValue = urlParams.get(key);
23
+ settingsToStore[key] = (paramValue === 'true') ? true : (paramValue === 'false' ? false : paramValue);
24
+ sourcedFromUrl = true;
25
+ }
26
+ }
27
+
28
+ if (sourcedFromUrl) {
29
+ // If any flight was set via URL, these override sessionStorage for this load
30
+ // and then update sessionStorage.
31
+ // Fill any missing flights with defaults before storing.
32
+ for (const key in defaultFlights) {
33
+ if (!Object.hasOwnProperty.call(settingsToStore, key)) {
34
+ settingsToStore[key] = defaultFlights[key];
35
+ }
36
+ }
37
+ try {
38
+ sessionStorage.setItem(FLIGHT_STORAGE_KEY, JSON.stringify(settingsToStore));
39
+ effectiveFlightSettings = settingsToStore;
40
+ } catch (e) {
41
+ console.error("Failed to write to sessionStorage:", e);
42
+ effectiveFlightSettings = settingsToStore; // Use them anyway for this page load
43
+ }
44
+ } else {
45
+ // No flight params in URL, try sessionStorage
46
+ try {
47
+ const storedSettings = sessionStorage.getItem(FLIGHT_STORAGE_KEY);
48
+ if (storedSettings) {
49
+ effectiveFlightSettings = JSON.parse(storedSettings);
50
+ // Ensure all default keys are present, add if missing (e.g., new flight added)
51
+ for (const key in defaultFlights) {
52
+ if (!Object.hasOwnProperty.call(effectiveFlightSettings, key)) {
53
+ effectiveFlightSettings[key] = defaultFlights[key];
54
+ }
55
+ }
56
+ } else {
57
+ // Not in sessionStorage, use defaults and store them
58
+ effectiveFlightSettings = { ...defaultFlights };
59
+ sessionStorage.setItem(FLIGHT_STORAGE_KEY, JSON.stringify(effectiveFlightSettings));
60
+ }
61
+ } catch (e) {
62
+ console.error("Failed to read/write sessionStorage:", e);
63
+ effectiveFlightSettings = { ...defaultFlights }; // Fallback to defaults
64
+ }
65
+ }
66
+ }
67
+
68
+ initializeFlightSettings();
69
+
70
+ // The `flights` object provides these effective values.
71
+ const flights = {};
72
+ for (const key in defaultFlights) { // Iterate defaultFlights to define getters for all known flights
73
+ if (Object.hasOwnProperty.call(defaultFlights, key)) {
74
+ Object.defineProperty(flights, key, {
75
+ get: () => effectiveFlightSettings[key] !== undefined ? effectiveFlightSettings[key] : defaultFlights[key],
76
+ enumerable: true,
77
+ configurable: false
78
+ });
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Helper to build a URLSearchParams object with the effective flight values.
84
+ * @returns {URLSearchParams}
85
+ */
86
+ function _buildFlightParams() {
87
+ const flightParams = new URLSearchParams();
88
+ for (const flightName in effectiveFlightSettings) {
89
+ if (Object.hasOwnProperty.call(effectiveFlightSettings, flightName)) {
90
+ // Only add if it's a recognized flight from defaultFlights to avoid polluting URL
91
+ // with unrelated sessionStorage items if FLIGHT_STORAGE_KEY was somehow corrupted.
92
+ if (Object.hasOwnProperty.call(defaultFlights, flightName)) {
93
+ flightParams.set(flightName, String(effectiveFlightSettings[flightName]));
94
+ }
95
+ }
96
+ }
97
+ return flightParams;
98
+ }
99
+
100
+ /**
101
+ * Constructs a URL with the effective flight parameters and any additional parameters.
102
+ * @param {string} baseUrl The base URL (e.g., "index.html").
103
+ * @param {object} [additionalParams={}] - An object of additional key-value pairs to include.
104
+ * @returns {string} A full URL with query string.
105
+ */
106
+ export function buildUrlWithFlights(baseUrl, additionalParams = {}) {
107
+ const params = _buildFlightParams(); // Start with all effective flight values
108
+
109
+ for (const key in additionalParams) {
110
+ if (Object.hasOwnProperty.call(additionalParams, key) && additionalParams[key] !== undefined) {
111
+ params.set(key, additionalParams[key]);
112
+ }
113
+ }
114
+ const queryString = params.toString();
115
+ return `${baseUrl}${queryString ? `?${queryString}` : ''}`;
116
+ }
117
+
118
+ /**
119
+ * Gets a query string containing only the effective flight parameters.
120
+ * @returns {string} A query string like "?enableOnboard=true" or empty.
121
+ */
122
+ export function getFlightQueryParameters() {
123
+ const queryString = _buildFlightParams().toString();
124
+ return queryString ? `?${queryString}` : '';
125
+ }
126
+
127
+ export default flights;
@@ -129,11 +129,11 @@ async function pollInstallStatus(categoryName, serverName, targets, interval = 2
129
129
  for (const req of requirements) {
130
130
  const reqStatus = requirementsStatus[req.name] || {};
131
131
  if (reqStatus.installed === true && !reqStatus.operationStatus) {
132
- const msg = `Requirement [${req.name}] already installed.`;
133
- if (msg && lastRequirementMessages[req.name] !== msg) {
134
- delayedAppendInstallLoadingMessage(msg);
135
- lastRequirementMessages[req.name] = msg;
136
- }
132
+ // const msg = `Requirement [${req.name}] already installed.`;
133
+ // if (msg && lastRequirementMessages[req.name] !== msg) {
134
+ // delayedAppendInstallLoadingMessage(msg);
135
+ // lastRequirementMessages[req.name] = msg;
136
+ // }
137
137
  continue;
138
138
  }
139
139
  const opStatus = reqStatus.operationStatus || {};
@@ -129,6 +129,7 @@ export function setupFormSubmitHandler(form, envInputsDiv, modalArguments, modal
129
129
  const envVars = {};
130
130
  const args = [];
131
131
  const requirementsToUpdate = [];
132
+ let pythonEnv = undefined;
132
133
 
133
134
  // Only collect env, args and requirements if not in SSE mode
134
135
  if (mcpServer?.mode !== 'sse') {
@@ -145,7 +146,7 @@ export function setupFormSubmitHandler(form, envInputsDiv, modalArguments, modal
145
146
  .filter(val => val !== ''));
146
147
 
147
148
  const pythonEnvInput = document.getElementById('python_env');
148
- const pythonEnv = pythonEnvInput?.value?.trim();
149
+ pythonEnv = pythonEnvInput?.value?.trim();
149
150
 
150
151
  const updateToggles = modalRequirements.querySelectorAll('.toggle-update:checked');
151
152
  updateToggles.forEach(toggle => {
@@ -178,7 +179,7 @@ export function setupFormSubmitHandler(form, envInputsDiv, modalArguments, modal
178
179
  targetClients: selectedTargets,
179
180
  env: envVars,
180
181
  args: args,
181
- settings: {}
182
+ settings: pythonEnv ? { pythonEnv: pythonEnv } : {}
182
183
  };
183
184
 
184
185
  if (requirementsToUpdate.length > 0) {
@@ -1,33 +1,68 @@
1
- // Function to show a Bootstrap alert notification
1
+ // Function to show a toast notification
2
2
  function showToast(message, type = 'success') {
3
3
  const alertContainer = document.querySelector('.alert-container');
4
+ if (!alertContainer) {
5
+ console.error('Alert container not found. Notifications will not be displayed.');
6
+ return;
7
+ }
4
8
  const alertId = `alert-${Date.now()}`;
5
-
9
+
10
+ let iconHtml = '';
11
+ switch (type) {
12
+ case 'success':
13
+ iconHtml = '<i class="bi bi-check-circle-fill alert-icon"></i>';
14
+ break;
15
+ case 'error':
16
+ iconHtml = '<i class="bi bi-x-octagon-fill alert-icon"></i>'; // Changed icon for error
17
+ break;
18
+ case 'info':
19
+ iconHtml = '<i class="bi bi-info-circle-fill alert-icon"></i>';
20
+ break;
21
+ case 'warning':
22
+ iconHtml = '<i class="bi bi-exclamation-triangle-fill alert-icon"></i>';
23
+ break;
24
+ default:
25
+ iconHtml = '<i class="bi bi-bell-fill alert-icon"></i>'; // Default icon
26
+ }
27
+
6
28
  const alertHtml = `
7
- <div id="${alertId}" class="alert alert-${type} alert-dismissible fade show" role="alert">
8
- ${type === 'success' ?
9
- '<i class="bi bi-check-circle-fill me-2"></i>' :
10
- '<i class="bi bi-exclamation-triangle-fill me-2"></i>'
11
- }
12
- ${message}
13
- <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
29
+ <div id="${alertId}" class="alert alert-${type}" role="alert">
30
+ ${iconHtml}
31
+ <span>${message}</span>
32
+ <button type="button" class="btn-close" aria-label="Close"></button>
14
33
  </div>
15
34
  `;
16
-
35
+
17
36
  alertContainer.insertAdjacentHTML('beforeend', alertHtml);
18
-
37
+
19
38
  const alertElement = document.getElementById(alertId);
20
-
21
- // Remove the alert after 5 seconds
22
- setTimeout(() => {
23
- const bsAlert = new bootstrap.Alert(alertElement);
24
- bsAlert.close();
25
- }, 3000); // Reduced to 3 seconds
26
-
27
- // Remove the element after animation
28
- alertElement.addEventListener('closed.bs.alert', () => {
29
- alertElement.remove();
39
+ if (!alertElement) return;
40
+
41
+ // Trigger the slide-in animation
42
+ // Adding a slight delay to ensure the element is in the DOM before adding the 'show' class
43
+ requestAnimationFrame(() => {
44
+ requestAnimationFrame(() => { // Double requestAnimationFrame for more reliable transition start
45
+ alertElement.classList.add('show');
46
+ });
30
47
  });
48
+
49
+
50
+ const closeButton = alertElement.querySelector('.btn-close');
51
+
52
+ const dismissAlert = () => {
53
+ alertElement.classList.remove('show'); // Trigger slide-out animation
54
+ // Wait for fade out animation to complete before removing
55
+ alertElement.addEventListener('transitionend', () => {
56
+ alertElement.remove();
57
+ }, { once: true }); // Ensure event listener is removed after execution
58
+ };
59
+
60
+ if (closeButton) {
61
+ closeButton.addEventListener('click', dismissAlert);
62
+ }
63
+
64
+ // Auto-dismiss after 5 seconds (increased from 3)
65
+ setTimeout(dismissAlert, 5000);
31
66
  }
32
67
 
33
68
  // Function to show a confirmation dialog using custom modal system
@@ -35,7 +70,7 @@ function showConfirm(title, message) {
35
70
  return new Promise((resolve) => {
36
71
  const modalId = `confirm-modal-${Date.now()}`;
37
72
  const modalHtml = `
38
- <div id="${modalId}" class="modal">
73
+ <div id="${modalId}" class="modal" style="display: none;">
39
74
  <div class="modal-content" style="max-width: 400px; margin: 15% auto;">
40
75
  <div class="modal-header">
41
76
  <h3 class="text-lg font-semibold text-gray-700">${title}</h3>
@@ -54,19 +89,23 @@ function showConfirm(title, message) {
54
89
  </div>
55
90
  </div>
56
91
  `;
57
-
92
+
58
93
  document.body.insertAdjacentHTML('beforeend', modalHtml);
59
-
94
+
60
95
  const modalElement = document.getElementById(modalId);
61
- modalElement.style.display = 'block';
62
-
96
+ // Trigger display after a short delay to allow CSS transitions if any are added later for modals
97
+ requestAnimationFrame(() => {
98
+ modalElement.style.display = 'block';
99
+ });
100
+
101
+
63
102
  // Handle confirm button click
64
103
  modalElement.querySelector('.confirm-button').addEventListener('click', () => {
65
104
  modalElement.style.display = 'none';
66
105
  modalElement.remove();
67
106
  resolve(true);
68
107
  });
69
-
108
+
70
109
  // Handle cancel button click
71
110
  modalElement.querySelector('.cancel-button').addEventListener('click', () => {
72
111
  modalElement.style.display = 'none';