imcp 0.2.2 → 0.2.4

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 (56) hide show
  1. package/README.md +8 -8
  2. package/dist/cli/commands/list-installed.d.ts +2 -0
  3. package/dist/cli/commands/list-installed.js +2 -0
  4. package/dist/cli/commands/list-installed.js.map +1 -0
  5. package/dist/cli/commands/update.js +1 -1
  6. package/dist/cli/commands/update.js.map +1 -1
  7. package/dist/cli/index.js +1 -1
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/cli/tui/components/ClientSelector.js +1 -1
  10. package/dist/cli/tui/components/ClientSelector.js.map +1 -1
  11. package/dist/core/installers/clients/BaseClientInstaller.js +1 -1
  12. package/dist/core/installers/clients/BaseClientInstaller.js.map +1 -1
  13. package/dist/core/installers/clients/ClientInstallerFactory.js +1 -1
  14. package/dist/core/installers/clients/ClientInstallerFactory.js.map +1 -1
  15. package/dist/core/installers/clients/CopilotCLIInstaller.d.ts +18 -0
  16. package/dist/core/installers/clients/CopilotCLIInstaller.js +2 -0
  17. package/dist/core/installers/clients/CopilotCLIInstaller.js.map +1 -0
  18. package/dist/core/installers/clients/ExtensionInstaller.d.ts +1 -0
  19. package/dist/core/installers/clients/ExtensionInstaller.js +1 -1
  20. package/dist/core/installers/clients/ExtensionInstaller.js.map +1 -1
  21. package/dist/core/installers/requirements/NpmInstaller.js +1 -1
  22. package/dist/core/installers/requirements/NpmInstaller.js.map +1 -1
  23. package/dist/core/metadatas/constants.d.ts +4 -0
  24. package/dist/core/metadatas/constants.js +1 -1
  25. package/dist/core/metadatas/constants.js.map +1 -1
  26. package/dist/utils/versionUtils.js +1 -1
  27. package/dist/utils/versionUtils.js.map +1 -1
  28. package/dist/web/public/js/api.js.map +1 -1
  29. package/dist/web/public/js/detailsWidget.js.map +1 -1
  30. package/dist/web/public/js/flights/flights.js.map +1 -1
  31. package/dist/web/public/js/modal/index.js.map +1 -1
  32. package/dist/web/public/js/modal/installModal.js.map +1 -1
  33. package/dist/web/public/js/modal/installation.js.map +1 -1
  34. package/dist/web/public/js/modal/loadingModal.js.map +1 -1
  35. package/dist/web/public/js/modal/modalSetup.js.map +1 -1
  36. package/dist/web/public/js/modal/modalUtils.js.map +1 -1
  37. package/dist/web/public/js/modal/versionUtils.js.map +1 -1
  38. package/dist/web/public/js/modal.js.map +1 -1
  39. package/dist/web/public/js/notifications.js.map +1 -1
  40. package/dist/web/public/js/onboard/formProcessor.js.map +1 -1
  41. package/dist/web/public/js/onboard/index.js.map +1 -1
  42. package/dist/web/public/js/onboard/publishHandler.js.map +1 -1
  43. package/dist/web/public/js/onboard/state.js.map +1 -1
  44. package/dist/web/public/js/onboard/templates.js.map +1 -1
  45. package/dist/web/public/js/onboard/uiHandlers.js.map +1 -1
  46. package/dist/web/public/js/onboard/validationHandlers.js.map +1 -1
  47. package/dist/web/public/js/serverCategoryDetails.js.map +1 -1
  48. package/dist/web/public/js/serverCategoryList.js.map +1 -1
  49. package/dist/web/public/js/settings.js.map +1 -1
  50. package/package.json +1 -1
  51. package/dist/core/installers/clients/CodexInstaller.d.ts +0 -45
  52. package/dist/core/installers/clients/CodexInstaller.js +0 -2
  53. package/dist/core/installers/clients/CodexInstaller.js.map +0 -1
  54. package/dist/utils/tomlUtils.d.ts +0 -17
  55. package/dist/utils/tomlUtils.js +0 -2
  56. package/dist/utils/tomlUtils.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"names":["fs","path","fileURLToPath","axios","Logger","COLORS","reset","yellow","PACKAGE_NAME","getPackageVersion","process","env","npm_package_version","packageName","packageVersion","currentDir","dirname","url","i","packagePath","join","existsSync","JSON","parse","readFileSync","version","error","console","compareVersions","v1","v2","v1Parts","split","map","Number","v2Parts","Math","max","length","v1Part","v2Part","async","checkForUpdates","debug","getAppVersion","availableUpdate","log","name","latestVersion","Error","message","String","npmResponse","get","data","latest","npmError"],"sources":["../../src/utils/versionUtils.ts"],"mappings":"OAAOA,OAAQ,YACRC,SAAU,cACRC,kBAAqB,aACvBC,UAAW,eACTC,WAAc,cAGvB,MAAMC,OAAS,CACbC,MAAO,OACPC,OAAQ,SAGJC,aAAe,cAEf,SAAUC,oBACd,IAEE,GAAIC,QAAQC,IAAIC,oBACd,MAAO,CAAEC,YANM,OAMqBC,eAAgBJ,QAAQC,IAAIC,qBAQlE,IAAIG,EAHcd,KAAKe,QAAQd,0BAA0Be,MAIzD,IAAK,IAAIC,EAAI,EAAGA,EAAI,EAAGA,IAAK,CAC1B,MAAMC,EAAclB,KAAKmB,KAAKL,EAAY,gBAC1C,GAAIf,GAAGqB,WAAWF,GAAc,CAE9B,MAAO,CAAEN,YAnBI,OAmBuBC,eADhBQ,KAAKC,MAAMvB,GAAGwB,aAAaL,EAAa,SACIM,QAClE,CACAV,EAAad,KAAKmB,KAAKL,EAAY,KACrC,CAEA,MAAO,CAAEF,YAxBQ,OAwBmBC,eAAgB,UACtD,CAAE,MAAOY,GAEP,OADAC,QAAQD,MAAM,iCAAkCA,GACzC,CAAEb,YA3BQ,OA2BmBC,eAAgB,UACtD,CACF,QAcM,SAAUc,gBAAgBC,EAAYC,GAC1C,MAAMC,EAAUF,EAAGG,MAAM,KAAKC,IAAIC,QAC5BC,EAAUL,EAAGE,MAAM,KAAKC,IAAIC,QAElC,IAAK,IAAIhB,EAAI,EAAGA,EAAIkB,KAAKC,IAAIN,EAAQO,OAAQH,EAAQG,QAASpB,IAAK,CACjE,MAAMqB,EAASrB,EAAIa,EAAQO,OAASP,EAAQb,GAAK,EAC3CsB,EAAStB,EAAIiB,EAAQG,OAASH,EAAQjB,GAAK,EAEjD,GAAIqB,IAAWC,EAGb,OAAOD,EAASC,CAEpB,CAEA,OAAO,CACT,QAKOC,eAAeC,kBACpBtC,OAAOuC,MAAM,2BACb,IACE,MAAMlB,QAAgBmB,gBAClBnB,EAAQoB,kBACVlB,QAAQmB,IAAI,GAAGzC,OAAOE,8BAA8BkB,EAAQsB,SAAStB,EAAQA,aAAaA,EAAQoB,gBAAgBG,gBAAgB3C,OAAOC,SACzIqB,QAAQmB,IAAI,GAAGzC,OAAOE,gCAAgCkB,EAAQsB,+DAA+DtB,EAAQsB,gBAAgB1C,OAAOC,SAGhK,CAAE,MAAOoB,GAEPtB,OAAOuC,MAAM,iCAAiCjB,aAAiBuB,MAAQvB,EAAMwB,QAAUC,OAAOzB,KAChG,CACF,QAMOe,eAAeG,gBACpB,IAAIC,EAEJ,MAAMhC,YAAEA,EAAWC,eAAEA,GAAmBL,oBAExC,IACE,MAAM2C,QAAoBjD,MAAMkD,IAAI,8BAA8BxC,KAClE,GAAIuC,EAAYE,MAAQF,EAAYE,KAAK,cAAgBF,EAAYE,KAAK,aAAaC,OAAQ,CAC7F,MAAMP,EAAgBI,EAAYE,KAAK,aAAaC,OAChD3B,gBAAgBoB,EAAelC,GAAkB,IACnD+B,EAAkB,CAAEG,gBAAeE,QAAS,gBAAgBF,2CAAuDnC,oBAEvH,CACF,CAAE,MAAO2C,GACPpD,OAAOuC,MAAM,+CAA+C9B,MAAgB2C,aAAoBP,MAAQO,EAASN,QAAUC,OAAOK,KACpI,CAEA,MAAO,CAAET,KAAMlC,EAAaY,QAASX,EAAgB+B,kBACvD","ignoreList":[]}
1
+ {"version":3,"names":["fs","path","fileURLToPath","axios","Logger","PUBLIC_NPM_REGISTRY","PUBLIC_NPM_REGISTRY_ARG","COLORS","reset","yellow","PACKAGE_NAME","getNpxRelaunchCommand","packageName","getNpmGlobalUpdateCommand","getPackageVersion","process","env","npm_package_version","packageVersion","currentDir","dirname","url","i","packagePath","join","existsSync","JSON","parse","readFileSync","version","error","console","compareVersions","v1","v2","v1Parts","split","map","Number","v2Parts","Math","max","length","v1Part","v2Part","async","checkForUpdates","debug","getAppVersion","availableUpdate","log","name","latestVersion","Error","message","String","npmResponse","get","data","latest","npmError"],"sources":["../../src/utils/versionUtils.ts"],"mappings":"OAAOA,OAAQ,YACRC,SAAU,cACRC,kBAAqB,aACvBC,UAAW,eACTC,WAAc,qBAErBC,oBACAC,4BACK,iCAGP,MAAMC,OAAS,CACbC,MAAO,OACPC,OAAQ,SAGJC,aAAe,OAErB,SAASC,sBAAsBC,GAC7B,MAAO,aAAaN,2BAA2BM,gBACjD,CAEA,SAASC,0BAA0BD,GACjC,MAAO,kBAAkBA,YAAsBN,yBACjD,QAEM,SAAUQ,oBACd,IAEE,GAAIC,QAAQC,IAAIC,oBACd,MAAO,CAAEL,YAdM,OAcqBM,eAAgBH,QAAQC,IAAIC,qBAQlE,IAAIE,EAHclB,KAAKmB,QAAQlB,0BAA0BmB,MAIzD,IAAK,IAAIC,EAAI,EAAGA,EAAI,EAAGA,IAAK,CAC1B,MAAMC,EAActB,KAAKuB,KAAKL,EAAY,gBAC1C,GAAInB,GAAGyB,WAAWF,GAAc,CAE9B,MAAO,CAAEX,YA3BI,OA2BuBM,eADhBQ,KAAKC,MAAM3B,GAAG4B,aAAaL,EAAa,SACIM,QAClE,CACAV,EAAalB,KAAKuB,KAAKL,EAAY,KACrC,CAEA,MAAO,CAAEP,YAhCQ,OAgCmBM,eAAgB,UACtD,CAAE,MAAOY,GAEP,OADAC,QAAQD,MAAM,iCAAkCA,GACzC,CAAElB,YAnCQ,OAmCmBM,eAAgB,UACtD,CACF,QAcM,SAAUc,gBAAgBC,EAAYC,GAC1C,MAAMC,EAAUF,EAAGG,MAAM,KAAKC,IAAIC,QAC5BC,EAAUL,EAAGE,MAAM,KAAKC,IAAIC,QAElC,IAAK,IAAIhB,EAAI,EAAGA,EAAIkB,KAAKC,IAAIN,EAAQO,OAAQH,EAAQG,QAASpB,IAAK,CACjE,MAAMqB,EAASrB,EAAIa,EAAQO,OAASP,EAAQb,GAAK,EAC3CsB,EAAStB,EAAIiB,EAAQG,OAASH,EAAQjB,GAAK,EAEjD,GAAIqB,IAAWC,EAGb,OAAOD,EAASC,CAEpB,CAEA,OAAO,CACT,QAKOC,eAAeC,kBACpB1C,OAAO2C,MAAM,2BACb,IACE,MAAMlB,QAAgBmB,gBAClBnB,EAAQoB,kBACVlB,QAAQmB,IAAI,GAAG3C,OAAOE,8BAA8BoB,EAAQsB,SAAStB,EAAQA,aAAaA,EAAQoB,gBAAgBG,gBAAgB7C,OAAOC,SACzIuB,QAAQmB,IAAI,GAAG3C,OAAOE,yBAAyBE,sBAAsBkB,EAAQsB,oCAAoCtC,0BAA0BgB,EAAQsB,UAAU5C,OAAOC,SAGxK,CAAE,MAAOsB,GAEP1B,OAAO2C,MAAM,iCAAiCjB,aAAiBuB,MAAQvB,EAAMwB,QAAUC,OAAOzB,KAChG,CACF,QAMOe,eAAeG,gBACpB,IAAIC,EAEJ,MAAMrC,YAAEA,EAAWM,eAAEA,GAAmBJ,oBAExC,IACE,MAAM0C,QAAoBrD,MAAMsD,IAAI,GAAGpD,sBAAsBO,KAC7D,GAAI4C,EAAYE,MAAQF,EAAYE,KAAK,cAAgBF,EAAYE,KAAK,aAAaC,OAAQ,CAC7F,MAAMP,EAAgBI,EAAYE,KAAK,aAAaC,OAChD3B,gBAAgBoB,EAAelC,GAAkB,IACnD+B,EAAkB,CAChBG,gBACAE,QAAS,gBAAgBF,oCAAgDzC,sBAAsBC,QAGrG,CACF,CAAE,MAAOgD,GACPxD,OAAO2C,MAAM,+CAA+CnC,MAAgBgD,aAAoBP,MAAQO,EAASN,QAAUC,OAAOK,KACpI,CAEA,MAAO,CAAET,KAAMvC,EAAaiB,QAASX,EAAgB+B,kBACvD","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["renderServerCategoryList"],"sources":["0"],"sourcesContent":["import { renderServerCategoryList } from './serverCategoryList.js';\r\nimport { showToast } from './notifications.js';\r\n\r\n// Global store for server data\r\nlet allServerCategoriesData = [];\r\n// Make it available globally for legacy code\r\nwindow.allServerCategoriesData = allServerCategoriesData;\r\n\r\n// Fetch and display servers\r\nasync function fetchServerCategories() {\r\n try {\r\n // Fetch all servers (without filters)\r\n const response = await fetch('/api/categories');\r\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);\r\n const responseData = await response.json();\r\n if (!responseData.success || !responseData.data) {\r\n throw new Error(`API Error fetching servers: ${responseData.error || 'Invalid data format'}`);\r\n }\r\n allServerCategoriesData = window.allServerCategoriesData = responseData.data; // Update both local and global references\r\n\r\n // Use the renderServerCategoryList function to display servers\r\n renderServerCategoryList(allServerCategoriesData);\r\n\r\n // Check if search is active\r\n const searchTerm = document.getElementById('searchBox').value.toLowerCase();\r\n if (searchTerm) {\r\n const filteredServerCategories = allServerCategoriesData.filter(server =>\r\n (server.displayName || server.name).toLowerCase().includes(searchTerm) ||\r\n (server.description || '').toLowerCase().includes(searchTerm)\r\n );\r\n renderServerCategoryList(filteredServerCategories);\r\n }\r\n } catch (error) {\r\n console.error('Error fetching servers:', error);\r\n document.getElementById('serverCategoryList').innerHTML =\r\n '<p class=\"text-red-600\">Error loading servers. Please check console and try again.</p>';\r\n document.getElementById('serverCategoryDetails').innerHTML = ''; // Clear details on error\r\n }\r\n}\r\n\r\n// Handle MCP tool installation\r\nasync function handleInstallServer(event, categoryName, serverName, callback, options = {}) {\r\n event.preventDefault(); // Prevent page reload\r\n console.log(\"Handling install for:\", categoryName, serverName);\r\n\r\n // Get selected targets from the form if not provided in options\r\n const selectedTargets = options.targets || [...document.querySelectorAll('input[name=\"targets\"]:checked')]\r\n .map(cb => cb.value);\r\n\r\n if (selectedTargets.length === 0) {\r\n showToast('Please select at least one target', 'error');\r\n return;\r\n }\r\n\r\n // Get environment variables from the form\r\n const form = event.target;\r\n const formData = new FormData(form);\r\n const envValues = {};\r\n formData.forEach((value, key) => {\r\n if (!key.startsWith('targets') && value) { // Skip target checkboxes\r\n envValues[key] = value;\r\n }\r\n });\r\n\r\n try {\r\n const serverList = {};\r\n serverList[serverName] = {\r\n env: envValues,\r\n targetClients: selectedTargets\r\n }; // Use serverName as key\r\n const response = await fetch(`/api/categories/${categoryName}/install`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ serverList: serverList })\r\n });\r\n\r\n if (!response.ok) {\r\n const errorData = await response.text();\r\n throw new Error(`Installation failed: ${errorData || response.statusText}`);\r\n }\r\n\r\n showToast('MCP server \"' + serverName + '\" for ' + categoryName + ' installation initiated successfully!', 'success');\r\n window.closeModal();\r\n fetchServerCategories(); // Refresh the main server list\r\n window.showServerDetails(categoryName); // Refresh the details pane\r\n\r\n } catch (error) {\r\n console.error('Error installing MCP server:', error);\r\n showToast('Error installing MCP server: ' + error.message + '. Please check console.', 'error');\r\n }\r\n}\r\n\r\n\r\nasync function uninstallTool(name) {\r\n const serverTitle = document.querySelector('#serverCategoryDetails h3')?.textContent;\r\n if (!serverTitle) {\r\n showToast('No server selected.', 'error');\r\n return;\r\n }\r\n\r\n const uninstallConfirmed = await showConfirm(`Do you want to uninstall tool '${name}' from ${serverTitle}?`);\r\n if (!uninstallConfirmed) {\r\n return;\r\n }\r\n\r\n try {\r\n const response = await fetch(`/api/categories/${serverTitle}/uninstall-tool`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ toolName: name })\r\n });\r\n\r\n if (!response.ok) {\r\n const errorData = await response.text();\r\n throw new Error(`Uninstall failed: ${errorData || response.statusText}`);\r\n }\r\n\r\n showToast(`Tool '${name}' uninstall initiated successfully!`, 'success');\r\n fetchServerCategories();\r\n window.showServerDetails(serverTitle);\r\n } catch (error) {\r\n console.error('Error uninstalling tool:', error);\r\n showToast(`Error uninstalling tool: ${error.message}`, 'error');\r\n }\r\n}\r\n\r\nexport {\r\n allServerCategoriesData,\r\n fetchServerCategories,\r\n handleInstallServer,\r\n uninstallTool\r\n};"],"mappings":"OAASA,6BAAgC,iC","ignoreList":[]}
1
+ {"version":3,"names":["renderServerCategoryList","showToast","allServerCategoriesData","async","fetchServerCategories","response","fetch","ok","Error","status","responseData","json","success","data","error","window","searchTerm","document","getElementById","value","toLowerCase","filteredServerCategories","filter","server","displayName","name","includes","description","console","innerHTML","handleInstallServer","event","categoryName","serverName","callback","options","preventDefault","log","selectedTargets","targets","querySelectorAll","map","cb","length","form","target","formData","FormData","envValues","forEach","key","startsWith","serverList","env","targetClients","method","headers","body","JSON","stringify","errorData","text","statusText","closeModal","showServerDetails","message","uninstallTool","serverTitle","querySelector","textContent","showConfirm","toolName"],"sources":["0"],"sourcesContent":["import { renderServerCategoryList } from './serverCategoryList.js';\r\nimport { showToast } from './notifications.js';\r\n\r\n// Global store for server data\r\nlet allServerCategoriesData = [];\r\n// Make it available globally for legacy code\r\nwindow.allServerCategoriesData = allServerCategoriesData;\r\n\r\n// Fetch and display servers\r\nasync function fetchServerCategories() {\r\n try {\r\n // Fetch all servers (without filters)\r\n const response = await fetch('/api/categories');\r\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);\r\n const responseData = await response.json();\r\n if (!responseData.success || !responseData.data) {\r\n throw new Error(`API Error fetching servers: ${responseData.error || 'Invalid data format'}`);\r\n }\r\n allServerCategoriesData = window.allServerCategoriesData = responseData.data; // Update both local and global references\r\n\r\n // Use the renderServerCategoryList function to display servers\r\n renderServerCategoryList(allServerCategoriesData);\r\n\r\n // Check if search is active\r\n const searchTerm = document.getElementById('searchBox').value.toLowerCase();\r\n if (searchTerm) {\r\n const filteredServerCategories = allServerCategoriesData.filter(server =>\r\n (server.displayName || server.name).toLowerCase().includes(searchTerm) ||\r\n (server.description || '').toLowerCase().includes(searchTerm)\r\n );\r\n renderServerCategoryList(filteredServerCategories);\r\n }\r\n } catch (error) {\r\n console.error('Error fetching servers:', error);\r\n document.getElementById('serverCategoryList').innerHTML =\r\n '<p class=\"text-red-600\">Error loading servers. Please check console and try again.</p>';\r\n document.getElementById('serverCategoryDetails').innerHTML = ''; // Clear details on error\r\n }\r\n}\r\n\r\n// Handle MCP tool installation\r\nasync function handleInstallServer(event, categoryName, serverName, callback, options = {}) {\r\n event.preventDefault(); // Prevent page reload\r\n console.log(\"Handling install for:\", categoryName, serverName);\r\n\r\n // Get selected targets from the form if not provided in options\r\n const selectedTargets = options.targets || [...document.querySelectorAll('input[name=\"targets\"]:checked')]\r\n .map(cb => cb.value);\r\n\r\n if (selectedTargets.length === 0) {\r\n showToast('Please select at least one target', 'error');\r\n return;\r\n }\r\n\r\n // Get environment variables from the form\r\n const form = event.target;\r\n const formData = new FormData(form);\r\n const envValues = {};\r\n formData.forEach((value, key) => {\r\n if (!key.startsWith('targets') && value) { // Skip target checkboxes\r\n envValues[key] = value;\r\n }\r\n });\r\n\r\n try {\r\n const serverList = {};\r\n serverList[serverName] = {\r\n env: envValues,\r\n targetClients: selectedTargets\r\n }; // Use serverName as key\r\n const response = await fetch(`/api/categories/${categoryName}/install`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ serverList: serverList })\r\n });\r\n\r\n if (!response.ok) {\r\n const errorData = await response.text();\r\n throw new Error(`Installation failed: ${errorData || response.statusText}`);\r\n }\r\n\r\n showToast('MCP server \"' + serverName + '\" for ' + categoryName + ' installation initiated successfully!', 'success');\r\n window.closeModal();\r\n fetchServerCategories(); // Refresh the main server list\r\n window.showServerDetails(categoryName); // Refresh the details pane\r\n\r\n } catch (error) {\r\n console.error('Error installing MCP server:', error);\r\n showToast('Error installing MCP server: ' + error.message + '. Please check console.', 'error');\r\n }\r\n}\r\n\r\n\r\nasync function uninstallTool(name) {\r\n const serverTitle = document.querySelector('#serverCategoryDetails h3')?.textContent;\r\n if (!serverTitle) {\r\n showToast('No server selected.', 'error');\r\n return;\r\n }\r\n\r\n const uninstallConfirmed = await showConfirm(`Do you want to uninstall tool '${name}' from ${serverTitle}?`);\r\n if (!uninstallConfirmed) {\r\n return;\r\n }\r\n\r\n try {\r\n const response = await fetch(`/api/categories/${serverTitle}/uninstall-tool`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ toolName: name })\r\n });\r\n\r\n if (!response.ok) {\r\n const errorData = await response.text();\r\n throw new Error(`Uninstall failed: ${errorData || response.statusText}`);\r\n }\r\n\r\n showToast(`Tool '${name}' uninstall initiated successfully!`, 'success');\r\n fetchServerCategories();\r\n window.showServerDetails(serverTitle);\r\n } catch (error) {\r\n console.error('Error uninstalling tool:', error);\r\n showToast(`Error uninstalling tool: ${error.message}`, 'error');\r\n }\r\n}\r\n\r\nexport {\r\n allServerCategoriesData,\r\n fetchServerCategories,\r\n handleInstallServer,\r\n uninstallTool\r\n};"],"mappings":"OAASA,6BAAgC,iCAChCC,cAAiB,qBAG1B,IAAIC,wBAA0B,GAK9BC,eAAeC,wBACX,IAEI,MAAMC,QAAiBC,MAAM,mBAC7B,IAAKD,EAASE,GAAI,MAAM,IAAIC,MAAM,uBAAuBH,EAASI,UAClE,MAAMC,QAAqBL,EAASM,OACpC,IAAKD,EAAaE,UAAYF,EAAaG,KACvC,MAAM,IAAIL,MAAM,+BAA+BE,EAAaI,OAAS,yBAEzEZ,wBAA0Ba,OAAOb,wBAA0BQ,EAAaG,KAGxEb,yBAAyBE,yBAGzB,MAAMc,EAAaC,SAASC,eAAe,aAAaC,MAAMC,cAC9D,GAAIJ,EAAY,CACZ,MAAMK,EAA2BnB,wBAAwBoB,QAAOC,IAC3DA,EAAOC,aAAeD,EAAOE,MAAML,cAAcM,SAASV,KAC1DO,EAAOI,aAAe,IAAIP,cAAcM,SAASV,KAEtDhB,yBAAyBqB,EAC7B,CACJ,CAAE,MAAOP,GACLc,QAAQd,MAAM,0BAA2BA,GACzCG,SAASC,eAAe,sBAAsBW,UAC1C,yFACJZ,SAASC,eAAe,yBAAyBW,UAAY,EACjE,CACJ,CAGA1B,eAAe2B,oBAAoBC,EAAOC,EAAcC,EAAYC,EAAUC,EAAU,CAAC,GACrFJ,EAAMK,iBACNR,QAAQS,IAAI,wBAAyBL,EAAcC,GAGnD,MAAMK,EAAkBH,EAAQI,SAAW,IAAItB,SAASuB,iBAAiB,kCACpEC,KAAIC,GAAMA,EAAGvB,QAElB,GAA+B,IAA3BmB,EAAgBK,OAEhB,YADA1C,UAAU,oCAAqC,SAKnD,MAAM2C,EAAOb,EAAMc,OACbC,EAAW,IAAIC,SAASH,GACxBI,EAAY,CAAC,EACnBF,EAASG,SAAQ,CAAC9B,EAAO+B,MAChBA,EAAIC,WAAW,YAAchC,IAC9B6B,EAAUE,GAAO/B,EACrB,IAGJ,IACI,MAAMiC,EAAa,CAAC,EACpBA,EAAWnB,GAAc,CACrBoB,IAAKL,EACLM,cAAehB,GAEnB,MAAMjC,QAAiBC,MAAM,mBAAmB0B,YAAwB,CACpEuB,OAAQ,OACRC,QAAS,CAAE,eAAgB,oBAC3BC,KAAMC,KAAKC,UAAU,CAAEP,WAAYA,MAGvC,IAAK/C,EAASE,GAAI,CACd,MAAMqD,QAAkBvD,EAASwD,OACjC,MAAM,IAAIrD,MAAM,wBAAwBoD,GAAavD,EAASyD,aAClE,CAEA7D,UAAU,eAAiBgC,EAAa,SAAWD,EAAe,wCAAyC,WAC3GjB,OAAOgD,aACP3D,wBACAW,OAAOiD,kBAAkBhC,EAE7B,CAAE,MAAOlB,GACLc,QAAQd,MAAM,+BAAgCA,GAC9Cb,UAAU,gCAAkCa,EAAMmD,QAAU,0BAA2B,QAC3F,CACJ,CAGA9D,eAAe+D,cAAczC,GACzB,MAAM0C,EAAclD,SAASmD,cAAc,8BAA8BC,YACzE,IAAKF,EAED,YADAlE,UAAU,sBAAuB,SAKrC,SADiCqE,YAAY,kCAAkC7C,WAAc0C,MAK7F,IACI,MAAM9D,QAAiBC,MAAM,mBAAmB6D,mBAA8B,CAC1EZ,OAAQ,OACRC,QAAS,CAAE,eAAgB,oBAC3BC,KAAMC,KAAKC,UAAU,CAAEY,SAAU9C,MAGrC,IAAKpB,EAASE,GAAI,CACd,MAAMqD,QAAkBvD,EAASwD,OACjC,MAAM,IAAIrD,MAAM,qBAAqBoD,GAAavD,EAASyD,aAC/D,CAEA7D,UAAU,SAASwB,uCAA2C,WAC9DrB,wBACAW,OAAOiD,kBAAkBG,EAC7B,CAAE,MAAOrD,GACLc,QAAQd,MAAM,2BAA4BA,GAC1Cb,UAAU,4BAA4Ba,EAAMmD,UAAW,QAC3D,CACJ,CAtHAlD,OAAOb,wBAA0BA,+BAyH7BA,wBACAE,sBACA0B,oBACAoC","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["DetailsWidget"],"sources":["0"],"sourcesContent":["export class DetailsWidget {\r\n constructor(container) {\r\n this.container = container;\r\n this.isExpanded = false;\r\n this.expandedTool = null;\r\n this.init();\r\n }\r\n\r\n init() {\r\n // Create and append stylesheet\r\n const link = document.createElement('link');\r\n link.rel = 'stylesheet';\r\n link.href = '/css/detailsWidget.css';\r\n document.head.appendChild(link);\r\n // Create wrapper to maintain proper width alignment\r\n this.containerWrapper = document.createElement('div');\r\n this.containerWrapper.className = 'details-widget-container';\r\n this.containerWrapper.style.width = '100%';\r\n this.containerWrapper.style.boxSizing = 'border-box';\r\n this.containerWrapper.style.maxWidth = '100%';\r\n this.containerWrapper.style.overflow = 'hidden';\r\n\r\n // Create the widget element\r\n this.widgetElement = document.createElement('div');\r\n this.widgetElement.className = 'details-widget';\r\n this.widgetElement.style.width = '100%';\r\n this.widgetElement.style.boxSizing = 'border-box';\r\n this.widgetElement.style.maxWidth = '100%';\r\n\r\n // Create the content element\r\n this.contentElement = document.createElement('div');\r\n this.contentElement.className = 'details-widget-content';\r\n this.contentElement.style.width = '100%';\r\n this.contentElement.style.boxSizing = 'border-box';\r\n\r\n // Build the DOM structure\r\n this.widgetElement.appendChild(this.contentElement);\r\n this.containerWrapper.appendChild(this.widgetElement);\r\n\r\n // Insert after the server item while maintaining proper structure\r\n this.container.after(this.containerWrapper);\r\n }\r\n\r\n setContent(content) {\r\n if (typeof content === 'string') {\r\n this.contentElement.innerHTML = `\r\n <div class=\"description-text\">\r\n ${content || 'No description available.'}\r\n </div>\r\n `;\r\n return;\r\n }\r\n\r\n // Handle direct schema object or wrapped schema object\r\n if (typeof content === 'object') {\r\n const schemaData = content.schema || content; // Handle both {schema: data} and direct data\r\n this.renderToolsList(schemaData);\r\n return;\r\n }\r\n\r\n this.contentElement.innerHTML = '<p>Invalid content format</p>';\r\n }\r\n\r\n // Maintain compatibility with old toggle behavior\r\n toggle() {\r\n if (this.isExpanded) {\r\n this.collapse();\r\n } else {\r\n this.expand();\r\n }\r\n }\r\n\r\n expand() {\r\n this.isExpanded = true;\r\n this.widgetElement.classList.add('expanded');\r\n\r\n // Ensure proper dimensions based on container\r\n const containerWidth = this.container.offsetWidth;\r\n this.containerWrapper.style.width = `${containerWidth}px`;\r\n this.containerWrapper.style.maxWidth = '100%';\r\n this.containerWrapper.style.display = 'block';\r\n\r\n // Add expanded state to container\r\n if (this.container) {\r\n this.container.classList.add('expanded');\r\n }\r\n }\r\n\r\n collapse() {\r\n this.isExpanded = false;\r\n this.widgetElement.classList.remove('expanded');\r\n if (this.container) {\r\n this.container.classList.remove('expanded');\r\n }\r\n // Reset expanded tool state\r\n this.expandedTool = null;\r\n }\r\n\r\n renderToolsList(schemaContent) {\r\n if (!schemaContent || typeof schemaContent !== 'object') {\r\n this.contentElement.innerHTML = '<p>Invalid schema format</p>';\r\n return;\r\n }\r\n\r\n let html = `\r\n <div class=\"tools-list\">\r\n `;\r\n\r\n Object.entries(schemaContent).forEach(([toolName, toolInfo]) => {\r\n if (!toolInfo) return;\r\n\r\n html += `<div class=\"tool-card\" data-tool=\"${this.escapeHtml(toolName)}\">\r\n <div class=\"tool-card-header\">\r\n <div class=\"tool-header\">\r\n <h3 class=\"text-md font-semibold text-blue-600\">${toolInfo.name || toolName}</h3>\r\n <div class=\"text-gray-600 text-sm\">${toolInfo.description || 'No description available'}</div>\r\n </div>\r\n </div>\r\n <div class=\"tool-details hidden\" data-tool-details=\"${this.escapeHtml(toolName)}\">\r\n ${this.renderInputSchema(toolInfo.inputSchema)}\r\n </div>\r\n </div>\r\n `;\r\n });\r\n\r\n html += `\r\n </div>\r\n `;\r\n\r\n this.contentElement.innerHTML = html;\r\n\r\n // Add event listeners to tool cards\r\n this.contentElement.querySelectorAll('.tool-card').forEach(card => {\r\n const header = card.querySelector('.tool-card-header');\r\n header.addEventListener('click', (e) => {\r\n e.stopPropagation(); // Prevent click from bubbling up to server-item\r\n const toolName = card.dataset.tool;\r\n this.toggleToolDetails(toolName, card);\r\n });\r\n\r\n // Add click handler for the details area to prevent bubbling\r\n const details = card.querySelector('.tool-details');\r\n details.addEventListener('click', (e) => {\r\n e.stopPropagation(); // Prevent click from bubbling up\r\n });\r\n });\r\n }\r\n\r\n toggleToolDetails(toolName, card) {\r\n const detailsElement = card.querySelector(`[data-tool-details=\"${toolName}\"]`);\r\n const isExpanded = !detailsElement.classList.contains('hidden');\r\n\r\n // Collapse all other expanded tools\r\n if (this.expandedTool && this.expandedTool !== toolName) {\r\n const prevCard = this.contentElement.querySelector(`[data-tool=\"${this.expandedTool}\"]`);\r\n if (prevCard) {\r\n const prevDetails = prevCard.querySelector(`[data-tool-details=\"${this.expandedTool}\"]`);\r\n prevDetails?.classList.add('hidden');\r\n prevCard.classList.remove('active');\r\n setTimeout(() => {\r\n prevDetails.classList.remove('visible');\r\n }, 0);\r\n }\r\n }\r\n\r\n // Toggle current tool with animation\r\n if (isExpanded) {\r\n detailsElement.classList.remove('visible');\r\n card.classList.remove('active');\r\n setTimeout(() => {\r\n detailsElement.classList.add('hidden');\r\n }, 300); // Match transition duration from CSS\r\n this.expandedTool = null;\r\n } else {\r\n detailsElement.classList.remove('hidden');\r\n card.classList.add('active');\r\n setTimeout(() => {\r\n detailsElement.classList.add('visible');\r\n }, 0);\r\n this.expandedTool = toolName;\r\n detailsElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\r\n }\r\n }\r\n\r\n renderInputSchema(schema) {\r\n if (!schema || typeof schema !== 'object') {\r\n return '<p>No input schema available</p>';\r\n }\r\n\r\n if (!schema.properties || Object.keys(schema.properties).length === 0) {\r\n return '<p>No input properties defined</p>';\r\n }\r\n\r\n let html = '<div class=\"properties-list\">';\r\n\r\n try {\r\n // Render each property\r\n Object.entries(schema.properties).forEach(([propName, propDetails]) => {\r\n if (!propDetails) return;\r\n\r\n const isRequired = schema.required?.includes(propName);\r\n const type = this.getPropertyType(propDetails);\r\n\r\n html += `<div class=\"property-item ${isRequired ? 'required' : ''}\">\r\n <div class=\"property-header\">\r\n <span class=\"property-name\">${this.escapeHtml(propName)}${isRequired ? '<span class=\"required-star\">*</span>' : ''}</span><span class=\"property-type\">${this.escapeHtml(type)}</span>${propDetails.description ? `<span class=\"property-desc\">${this.escapeHtml(propDetails.description)}</span>` : ''}\r\n ${propDetails.default !== undefined ? `<div class=\"property-default\">Default: <code>${this.escapeHtml(JSON.stringify(propDetails.default))}</code></div>` : ''}\r\n </div>\r\n ${this.renderNestedProperties(propDetails)}\r\n </div>\r\n `;\r\n });\r\n\r\n return html + '</div>';\r\n } catch (error) {\r\n console.error('Error rendering input schema:', error);\r\n return '<p>Error rendering input schema</p>';\r\n }\r\n }\r\n\r\n renderNestedProperties(propDetails) {\r\n if (propDetails.properties) {\r\n let html = '<div class=\"nested-properties\">';\r\n Object.entries(propDetails.properties).forEach(([name, details]) => {\r\n const type = this.getPropertyType(details);\r\n html += `\r\n <div class=\"nested-property-item\">\r\n <span class=\"property-name\">${this.escapeHtml(name)}</span><span class=\"property-type\">${this.escapeHtml(type)}</span>\r\n ${details.description ? `<div class=\"property-desc\">${this.escapeHtml(details.description)}</div>` : ''}\r\n </div>\r\n `;\r\n });\r\n return html + '</div>';\r\n }\r\n return '';\r\n }\r\n\r\n getPropertyType(propDetails) {\r\n if (propDetails.anyOf) {\r\n return `oneOf: [${propDetails.anyOf.map(type => type.type || 'any').join(', ')}]`;\r\n }\r\n if (propDetails.type === 'array') {\r\n const itemType = propDetails.items?.type || 'any';\r\n return `array<${itemType}>`;\r\n }\r\n return propDetails.type || 'any';\r\n }\r\n\r\n escapeHtml(unsafe) {\r\n if (unsafe === undefined || unsafe === null) {\r\n return '';\r\n }\r\n return String(unsafe)\r\n .replace(/&/g, \"&amp;\")\r\n .replace(/</g, \"&lt;\")\r\n .replace(/>/g, \"&gt;\")\r\n .replace(/\"/g, \"&quot;\")\r\n .replace(/'/g, \"&#039;\");\r\n }\r\n\r\n isVisible() {\r\n return this.isExpanded;\r\n }\r\n}"],"mappings":"OAAO,MAAMA,c","ignoreList":[]}
1
+ {"version":3,"names":["DetailsWidget","constructor","container","this","isExpanded","expandedTool","init","link","document","createElement","rel","href","head","appendChild","containerWrapper","className","style","width","boxSizing","maxWidth","overflow","widgetElement","contentElement","after","setContent","content","innerHTML","schemaData","schema","renderToolsList","toggle","collapse","expand","classList","add","containerWidth","offsetWidth","display","remove","schemaContent","html","Object","entries","forEach","toolName","toolInfo","escapeHtml","name","description","renderInputSchema","inputSchema","querySelectorAll","card","querySelector","addEventListener","e","stopPropagation","dataset","tool","toggleToolDetails","detailsElement","contains","prevCard","prevDetails","setTimeout","scrollIntoView","behavior","block","properties","keys","length","propName","propDetails","isRequired","required","includes","type","getPropertyType","undefined","default","JSON","stringify","renderNestedProperties","error","console","details","anyOf","map","join","items","unsafe","String","replace","isVisible"],"sources":["0"],"sourcesContent":["export class DetailsWidget {\r\n constructor(container) {\r\n this.container = container;\r\n this.isExpanded = false;\r\n this.expandedTool = null;\r\n this.init();\r\n }\r\n\r\n init() {\r\n // Create and append stylesheet\r\n const link = document.createElement('link');\r\n link.rel = 'stylesheet';\r\n link.href = '/css/detailsWidget.css';\r\n document.head.appendChild(link);\r\n // Create wrapper to maintain proper width alignment\r\n this.containerWrapper = document.createElement('div');\r\n this.containerWrapper.className = 'details-widget-container';\r\n this.containerWrapper.style.width = '100%';\r\n this.containerWrapper.style.boxSizing = 'border-box';\r\n this.containerWrapper.style.maxWidth = '100%';\r\n this.containerWrapper.style.overflow = 'hidden';\r\n\r\n // Create the widget element\r\n this.widgetElement = document.createElement('div');\r\n this.widgetElement.className = 'details-widget';\r\n this.widgetElement.style.width = '100%';\r\n this.widgetElement.style.boxSizing = 'border-box';\r\n this.widgetElement.style.maxWidth = '100%';\r\n\r\n // Create the content element\r\n this.contentElement = document.createElement('div');\r\n this.contentElement.className = 'details-widget-content';\r\n this.contentElement.style.width = '100%';\r\n this.contentElement.style.boxSizing = 'border-box';\r\n\r\n // Build the DOM structure\r\n this.widgetElement.appendChild(this.contentElement);\r\n this.containerWrapper.appendChild(this.widgetElement);\r\n\r\n // Insert after the server item while maintaining proper structure\r\n this.container.after(this.containerWrapper);\r\n }\r\n\r\n setContent(content) {\r\n if (typeof content === 'string') {\r\n this.contentElement.innerHTML = `\r\n <div class=\"description-text\">\r\n ${content || 'No description available.'}\r\n </div>\r\n `;\r\n return;\r\n }\r\n\r\n // Handle direct schema object or wrapped schema object\r\n if (typeof content === 'object') {\r\n const schemaData = content.schema || content; // Handle both {schema: data} and direct data\r\n this.renderToolsList(schemaData);\r\n return;\r\n }\r\n\r\n this.contentElement.innerHTML = '<p>Invalid content format</p>';\r\n }\r\n\r\n // Maintain compatibility with old toggle behavior\r\n toggle() {\r\n if (this.isExpanded) {\r\n this.collapse();\r\n } else {\r\n this.expand();\r\n }\r\n }\r\n\r\n expand() {\r\n this.isExpanded = true;\r\n this.widgetElement.classList.add('expanded');\r\n\r\n // Ensure proper dimensions based on container\r\n const containerWidth = this.container.offsetWidth;\r\n this.containerWrapper.style.width = `${containerWidth}px`;\r\n this.containerWrapper.style.maxWidth = '100%';\r\n this.containerWrapper.style.display = 'block';\r\n\r\n // Add expanded state to container\r\n if (this.container) {\r\n this.container.classList.add('expanded');\r\n }\r\n }\r\n\r\n collapse() {\r\n this.isExpanded = false;\r\n this.widgetElement.classList.remove('expanded');\r\n if (this.container) {\r\n this.container.classList.remove('expanded');\r\n }\r\n // Reset expanded tool state\r\n this.expandedTool = null;\r\n }\r\n\r\n renderToolsList(schemaContent) {\r\n if (!schemaContent || typeof schemaContent !== 'object') {\r\n this.contentElement.innerHTML = '<p>Invalid schema format</p>';\r\n return;\r\n }\r\n\r\n let html = `\r\n <div class=\"tools-list\">\r\n `;\r\n\r\n Object.entries(schemaContent).forEach(([toolName, toolInfo]) => {\r\n if (!toolInfo) return;\r\n\r\n html += `<div class=\"tool-card\" data-tool=\"${this.escapeHtml(toolName)}\">\r\n <div class=\"tool-card-header\">\r\n <div class=\"tool-header\">\r\n <h3 class=\"text-md font-semibold text-blue-600\">${toolInfo.name || toolName}</h3>\r\n <div class=\"text-gray-600 text-sm\">${toolInfo.description || 'No description available'}</div>\r\n </div>\r\n </div>\r\n <div class=\"tool-details hidden\" data-tool-details=\"${this.escapeHtml(toolName)}\">\r\n ${this.renderInputSchema(toolInfo.inputSchema)}\r\n </div>\r\n </div>\r\n `;\r\n });\r\n\r\n html += `\r\n </div>\r\n `;\r\n\r\n this.contentElement.innerHTML = html;\r\n\r\n // Add event listeners to tool cards\r\n this.contentElement.querySelectorAll('.tool-card').forEach(card => {\r\n const header = card.querySelector('.tool-card-header');\r\n header.addEventListener('click', (e) => {\r\n e.stopPropagation(); // Prevent click from bubbling up to server-item\r\n const toolName = card.dataset.tool;\r\n this.toggleToolDetails(toolName, card);\r\n });\r\n\r\n // Add click handler for the details area to prevent bubbling\r\n const details = card.querySelector('.tool-details');\r\n details.addEventListener('click', (e) => {\r\n e.stopPropagation(); // Prevent click from bubbling up\r\n });\r\n });\r\n }\r\n\r\n toggleToolDetails(toolName, card) {\r\n const detailsElement = card.querySelector(`[data-tool-details=\"${toolName}\"]`);\r\n const isExpanded = !detailsElement.classList.contains('hidden');\r\n\r\n // Collapse all other expanded tools\r\n if (this.expandedTool && this.expandedTool !== toolName) {\r\n const prevCard = this.contentElement.querySelector(`[data-tool=\"${this.expandedTool}\"]`);\r\n if (prevCard) {\r\n const prevDetails = prevCard.querySelector(`[data-tool-details=\"${this.expandedTool}\"]`);\r\n prevDetails?.classList.add('hidden');\r\n prevCard.classList.remove('active');\r\n setTimeout(() => {\r\n prevDetails.classList.remove('visible');\r\n }, 0);\r\n }\r\n }\r\n\r\n // Toggle current tool with animation\r\n if (isExpanded) {\r\n detailsElement.classList.remove('visible');\r\n card.classList.remove('active');\r\n setTimeout(() => {\r\n detailsElement.classList.add('hidden');\r\n }, 300); // Match transition duration from CSS\r\n this.expandedTool = null;\r\n } else {\r\n detailsElement.classList.remove('hidden');\r\n card.classList.add('active');\r\n setTimeout(() => {\r\n detailsElement.classList.add('visible');\r\n }, 0);\r\n this.expandedTool = toolName;\r\n detailsElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\r\n }\r\n }\r\n\r\n renderInputSchema(schema) {\r\n if (!schema || typeof schema !== 'object') {\r\n return '<p>No input schema available</p>';\r\n }\r\n\r\n if (!schema.properties || Object.keys(schema.properties).length === 0) {\r\n return '<p>No input properties defined</p>';\r\n }\r\n\r\n let html = '<div class=\"properties-list\">';\r\n\r\n try {\r\n // Render each property\r\n Object.entries(schema.properties).forEach(([propName, propDetails]) => {\r\n if (!propDetails) return;\r\n\r\n const isRequired = schema.required?.includes(propName);\r\n const type = this.getPropertyType(propDetails);\r\n\r\n html += `<div class=\"property-item ${isRequired ? 'required' : ''}\">\r\n <div class=\"property-header\">\r\n <span class=\"property-name\">${this.escapeHtml(propName)}${isRequired ? '<span class=\"required-star\">*</span>' : ''}</span><span class=\"property-type\">${this.escapeHtml(type)}</span>${propDetails.description ? `<span class=\"property-desc\">${this.escapeHtml(propDetails.description)}</span>` : ''}\r\n ${propDetails.default !== undefined ? `<div class=\"property-default\">Default: <code>${this.escapeHtml(JSON.stringify(propDetails.default))}</code></div>` : ''}\r\n </div>\r\n ${this.renderNestedProperties(propDetails)}\r\n </div>\r\n `;\r\n });\r\n\r\n return html + '</div>';\r\n } catch (error) {\r\n console.error('Error rendering input schema:', error);\r\n return '<p>Error rendering input schema</p>';\r\n }\r\n }\r\n\r\n renderNestedProperties(propDetails) {\r\n if (propDetails.properties) {\r\n let html = '<div class=\"nested-properties\">';\r\n Object.entries(propDetails.properties).forEach(([name, details]) => {\r\n const type = this.getPropertyType(details);\r\n html += `\r\n <div class=\"nested-property-item\">\r\n <span class=\"property-name\">${this.escapeHtml(name)}</span><span class=\"property-type\">${this.escapeHtml(type)}</span>\r\n ${details.description ? `<div class=\"property-desc\">${this.escapeHtml(details.description)}</div>` : ''}\r\n </div>\r\n `;\r\n });\r\n return html + '</div>';\r\n }\r\n return '';\r\n }\r\n\r\n getPropertyType(propDetails) {\r\n if (propDetails.anyOf) {\r\n return `oneOf: [${propDetails.anyOf.map(type => type.type || 'any').join(', ')}]`;\r\n }\r\n if (propDetails.type === 'array') {\r\n const itemType = propDetails.items?.type || 'any';\r\n return `array<${itemType}>`;\r\n }\r\n return propDetails.type || 'any';\r\n }\r\n\r\n escapeHtml(unsafe) {\r\n if (unsafe === undefined || unsafe === null) {\r\n return '';\r\n }\r\n return String(unsafe)\r\n .replace(/&/g, \"&amp;\")\r\n .replace(/</g, \"&lt;\")\r\n .replace(/>/g, \"&gt;\")\r\n .replace(/\"/g, \"&quot;\")\r\n .replace(/'/g, \"&#039;\");\r\n }\r\n\r\n isVisible() {\r\n return this.isExpanded;\r\n }\r\n}"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":[],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":["FLIGHT_STORAGE_KEY","defaultFlights","enableOnboard","effectiveFlightSettings","initializeFlightSettings","urlParams","URLSearchParams","window","location","search","settingsToStore","sourcedFromUrl","key","has","paramValue","get","Object","hasOwnProperty","call","sessionStorage","setItem","JSON","stringify","e","console","error","storedSettings","getItem","parse","flights","defineProperty","undefined","enumerable","configurable","_buildFlightParams","flightParams","flightName","set","String","buildUrlWithFlights","baseUrl","additionalParams","params","queryString","toString","getFlightQueryParameters"],"sources":["0"],"sourcesContent":["// Centralized flight control configuration\n\nconst FLIGHT_STORAGE_KEY = 'activeFlightSettings';\n\n// Default flight settings\nconst defaultFlights = {\n enableOnboard: true, // Default: Onboarding is enabled. Set to false to disable.\n // Add other default flights here, e.g., newFeatureX: false,\n};\n\nlet effectiveFlightSettings = {};\n\n// Initialize flight settings\nfunction initializeFlightSettings() {\n const urlParams = new URLSearchParams(window.location.search);\n let settingsToStore = {};\n let sourcedFromUrl = false;\n\n // Check URL parameters first\n for (const key in defaultFlights) {\n if (urlParams.has(key)) {\n const paramValue = urlParams.get(key);\n settingsToStore[key] = (paramValue === 'true') ? true : (paramValue === 'false' ? false : paramValue);\n sourcedFromUrl = true;\n }\n }\n\n if (sourcedFromUrl) {\n // If any flight was set via URL, these override sessionStorage for this load\n // and then update sessionStorage.\n // Fill any missing flights with defaults before storing.\n for (const key in defaultFlights) {\n if (!Object.hasOwnProperty.call(settingsToStore, key)) {\n settingsToStore[key] = defaultFlights[key];\n }\n }\n try {\n sessionStorage.setItem(FLIGHT_STORAGE_KEY, JSON.stringify(settingsToStore));\n effectiveFlightSettings = settingsToStore;\n } catch (e) {\n console.error(\"Failed to write to sessionStorage:\", e);\n effectiveFlightSettings = settingsToStore; // Use them anyway for this page load\n }\n } else {\n // No flight params in URL, try sessionStorage\n try {\n const storedSettings = sessionStorage.getItem(FLIGHT_STORAGE_KEY);\n if (storedSettings) {\n effectiveFlightSettings = JSON.parse(storedSettings);\n // Ensure all default keys are present, add if missing (e.g., new flight added)\n for (const key in defaultFlights) {\n if (!Object.hasOwnProperty.call(effectiveFlightSettings, key)) {\n effectiveFlightSettings[key] = defaultFlights[key];\n }\n }\n } else {\n // Not in sessionStorage, use defaults and store them\n effectiveFlightSettings = { ...defaultFlights };\n sessionStorage.setItem(FLIGHT_STORAGE_KEY, JSON.stringify(effectiveFlightSettings));\n }\n } catch (e) {\n console.error(\"Failed to read/write sessionStorage:\", e);\n effectiveFlightSettings = { ...defaultFlights }; // Fallback to defaults\n }\n }\n}\n\ninitializeFlightSettings();\n\n// The `flights` object provides these effective values.\nconst flights = {};\nfor (const key in defaultFlights) { // Iterate defaultFlights to define getters for all known flights\n if (Object.hasOwnProperty.call(defaultFlights, key)) {\n Object.defineProperty(flights, key, {\n get: () => effectiveFlightSettings[key] !== undefined ? effectiveFlightSettings[key] : defaultFlights[key],\n enumerable: true,\n configurable: false\n });\n }\n}\n\n/**\n * Helper to build a URLSearchParams object with the effective flight values.\n * @returns {URLSearchParams}\n */\nfunction _buildFlightParams() {\n const flightParams = new URLSearchParams();\n for (const flightName in effectiveFlightSettings) {\n if (Object.hasOwnProperty.call(effectiveFlightSettings, flightName)) {\n // Only add if it's a recognized flight from defaultFlights to avoid polluting URL\n // with unrelated sessionStorage items if FLIGHT_STORAGE_KEY was somehow corrupted.\n if (Object.hasOwnProperty.call(defaultFlights, flightName)) {\n flightParams.set(flightName, String(effectiveFlightSettings[flightName]));\n }\n }\n }\n return flightParams;\n}\n\n/**\n * Constructs a URL with the effective flight parameters and any additional parameters.\n * @param {string} baseUrl The base URL (e.g., \"index.html\").\n * @param {object} [additionalParams={}] - An object of additional key-value pairs to include.\n * @returns {string} A full URL with query string.\n */\nexport function buildUrlWithFlights(baseUrl, additionalParams = {}) {\n const params = _buildFlightParams(); // Start with all effective flight values\n\n for (const key in additionalParams) {\n if (Object.hasOwnProperty.call(additionalParams, key) && additionalParams[key] !== undefined) {\n params.set(key, additionalParams[key]);\n }\n }\n const queryString = params.toString();\n return `${baseUrl}${queryString ? `?${queryString}` : ''}`;\n}\n\n/**\n * Gets a query string containing only the effective flight parameters.\n * @returns {string} A query string like \"?enableOnboard=true\" or empty.\n */\nexport function getFlightQueryParameters() {\n const queryString = _buildFlightParams().toString();\n return queryString ? `?${queryString}` : '';\n}\n\nexport default flights;"],"mappings":"AAEA,MAAMA,mBAAqB,uBAGrBC,eAAiB,CACnBC,eAAe,GAInB,IAAIC,wBAA0B,CAAC,EAG/B,SAASC,2BACL,MAAMC,EAAY,IAAIC,gBAAgBC,OAAOC,SAASC,QACtD,IAAIC,EAAkB,CAAC,EACnBC,GAAiB,EAGrB,IAAK,MAAMC,KAAOX,eACd,GAAII,EAAUQ,IAAID,GAAM,CACpB,MAAME,EAAaT,EAAUU,IAAIH,GACjCF,EAAgBE,GAAuB,SAAfE,GAAgD,UAAfA,GAAiCA,EAC1FH,GAAiB,CACrB,CAGJ,GAAIA,EAAgB,CAIhB,IAAK,MAAMC,KAAOX,eACTe,OAAOC,eAAeC,KAAKR,EAAiBE,KAC7CF,EAAgBE,GAAOX,eAAeW,IAG9C,IACIO,eAAeC,QAAQpB,mBAAoBqB,KAAKC,UAAUZ,IAC1DP,wBAA0BO,CAC9B,CAAE,MAAOa,GACLC,QAAQC,MAAM,qCAAsCF,GACpDpB,wBAA0BO,CAC9B,CACJ,MAEI,IACI,MAAMgB,EAAiBP,eAAeQ,QAAQ3B,oBAC9C,GAAI0B,EAAgB,CAChBvB,wBAA0BkB,KAAKO,MAAMF,GAErC,IAAK,MAAMd,KAAOX,eACTe,OAAOC,eAAeC,KAAKf,wBAAyBS,KACrDT,wBAAwBS,GAAOX,eAAeW,GAG1D,MAEIT,wBAA0B,IAAKF,gBAC/BkB,eAAeC,QAAQpB,mBAAoBqB,KAAKC,UAAUnB,yBAElE,CAAE,MAAOoB,GACLC,QAAQC,MAAM,uCAAwCF,GACtDpB,wBAA0B,IAAKF,eACnC,CAER,CAEAG,2BAGA,MAAMyB,QAAU,CAAC,EACjB,IAAK,MAAMjB,KAAOX,eACVe,OAAOC,eAAeC,KAAKjB,eAAgBW,IAC3CI,OAAOc,eAAeD,QAASjB,EAAK,CAChCG,IAAK,SAAuCgB,IAAjC5B,wBAAwBS,GAAqBT,wBAAwBS,GAAOX,eAAeW,GACtGoB,YAAY,EACZC,cAAc,IAS1B,SAASC,qBACL,MAAMC,EAAe,IAAI7B,gBACzB,IAAK,MAAM8B,KAAcjC,wBACjBa,OAAOC,eAAeC,KAAKf,wBAAyBiC,IAGhDpB,OAAOC,eAAeC,KAAKjB,eAAgBmC,IAC3CD,EAAaE,IAAID,EAAYE,OAAOnC,wBAAwBiC,KAIxE,OAAOD,CACX,QAQO,SAASI,oBAAoBC,EAASC,EAAmB,CAAC,GAC7D,MAAMC,EAASR,qBAEf,IAAK,MAAMtB,KAAO6B,EACVzB,OAAOC,eAAeC,KAAKuB,EAAkB7B,SAAkCmB,IAA1BU,EAAiB7B,IACtE8B,EAAOL,IAAIzB,EAAK6B,EAAiB7B,IAGzC,MAAM+B,EAAcD,EAAOE,WAC3B,MAAO,GAAGJ,IAAUG,EAAc,IAAIA,IAAgB,IAC1D,QAMO,SAASE,2BACZ,MAAMF,EAAcT,qBAAqBU,WACzC,OAAOD,EAAc,IAAIA,IAAgB,EAC7C,gBAEed","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":[],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":["compareVersions","showInstallLoadingModal","hideInstallLoadingModal","updateOverallInstallStatus","addInstallationStep","closeModal","handleBulkClientInstall","uninstallTools","showInstallModal","setupClientItems","setupEnvironmentVariables","setupInstallationArguments","setupServerRequirements","setupFormSubmitHandler","window"],"sources":["0"],"sourcesContent":["// Import all modal-related functionality\r\nimport { compareVersions } from './versionUtils.js';\r\n// import { delayedAppendInstallLoadingMessage } from './messageQueue.js'; // Removed\r\nimport { showInstallLoadingModal, hideInstallLoadingModal, updateOverallInstallStatus, addInstallationStep } from './loadingModal.js'; // Removed appendInstallLoadingMessage, added new fns\r\nimport { closeModal } from './modalUtils.js';\r\nimport { handleBulkClientInstall, uninstallTools } from './installation.js';\r\nimport { showInstallModal } from './installModal.js';\r\nimport {\r\n setupClientItems,\r\n setupEnvironmentVariables,\r\n setupInstallationArguments,\r\n setupServerRequirements,\r\n setupFormSubmitHandler\r\n} from './modalSetup.js';\r\n\r\n// Export all modal functionality\r\nexport {\r\n // Version utilities\r\n compareVersions,\r\n\r\n // Message queue - (delayedAppendInstallLoadingMessage removed)\r\n\r\n // Loading modal\r\n showInstallLoadingModal,\r\n // appendInstallLoadingMessage, // Removed\r\n hideInstallLoadingModal,\r\n updateOverallInstallStatus, // Added\r\n addInstallationStep, // Added\r\n\r\n // Modal utilities\r\n closeModal,\r\n // Installation\r\n handleBulkClientInstall,\r\n uninstallTools,\r\n\r\n // Install modal\r\n showInstallModal,\r\n\r\n // Modal setup\r\n setupClientItems,\r\n setupEnvironmentVariables,\r\n setupInstallationArguments,\r\n setupServerRequirements,\r\n setupFormSubmitHandler\r\n};\r\n\r\n// Make certain functions available globally\r\nwindow.showInstallModal = showInstallModal;\r\nwindow.showInstallLoadingModal = showInstallLoadingModal;\r\n// window.appendInstallLoadingMessage = appendInstallLoadingMessage; // Removed\r\nwindow.hideInstallLoadingModal = hideInstallLoadingModal;\r\nwindow.uninstallTools = uninstallTools;"],"mappings":"OACSA,oBAAuB,2BAEvBC,wBAAyBC,wBAAyBC,2BAA4BC,wBAA2B,2BACzGC,eAAkB,yBAClBC,wBAAyBC,mBAAsB,2BAC/CC,qBAAwB,2BAE7BC,iBACAC,0BACAC,2BACAC,wBACAC,2BACG,yBAKHb,gBAKAC,wBAEAC,wBACAC,2BACAC,oBAGAC,WAEAC,wBACAC,eAGAC,iBAGAC,iBACAC,0BACAC,2BACAC,wBACAC,wBAIJC,OAAON,iBAAmBA,iBAC1BM,OAAOb,wBAA0BA,wBAEjCa,OAAOZ,wBAA0BA,wBACjCY,OAAOP,eAAiBA","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["showToast","showConfirm"],"sources":["0"],"sourcesContent":["import { showToast, showConfirm } from '../notifications.js';\r\nimport { compareVersions } from './versionUtils.js';\r\n// import { delayedAppendInstallLoadingMessage } from './messageQueue.js'; // No longer needed here\r\nimport { showInstallLoadingModal } from './loadingModal.js';\r\nimport { closeModal, setupToggleStyles } from './modalUtils.js';\r\nimport { handleBulkClientInstall, uninstallTools } from './installation.js';\r\nimport {\r\n setupClientItems,\r\n setupEnvironmentVariables,\r\n setupInstallationArguments,\r\n setupServerRequirements,\r\n setupFormSubmitHandler\r\n} from './modalSetup.js';\r\n\r\n// Initialize modal functionality when DOM is loaded\r\ndocument.addEventListener('DOMContentLoaded', () => {\r\n setupToggleStyles();\r\n});\r\n\r\n/**\r\n * Show install modal for MCP tools\r\n * @param {string} categoryName - The category name\r\n * @param {string} serverName - The server name\r\n * @param {Function} callback - Optional callback function\r\n */\r\nexport async function showInstallModal(categoryName, serverName, callback) {\r\n console.log(\"Showing install modal for:\", serverName);\r\n\r\n // Wait for a short delay to ensure modal is loaded\r\n await new Promise(resolve => setTimeout(resolve, 100));\r\n\r\n const modal = document.getElementById('installModal');\r\n if (!modal) {\r\n console.error('Modal container not found');\r\n return;\r\n }\r\n\r\n const title = modal.querySelector('#modalTitle');\r\n const envInputsDiv = modal.querySelector('#modalEnvInputs');\r\n const targetDiv = modal.querySelector('#modalTargets');\r\n const modalRequirements = modal.querySelector('#modalRequirements');\r\n const modalArguments = modal.querySelector('#modalArguments');\r\n\r\n // Global array to track selected clients\r\n window.selectedClients = [];\r\n\r\n // Verify all required modal elements exist\r\n if (!title || !envInputsDiv || !targetDiv || !modalRequirements || !modalArguments) {\r\n console.error('Required modal elements not found');\r\n return;\r\n }\r\n\r\n title.textContent = `Install ${serverName}`;\r\n envInputsDiv.innerHTML = ''; // Clear previous inputs\r\n targetDiv.innerHTML = ''; // Clear previous targets\r\n modalRequirements.innerHTML = ''; // Clear previous requirements\r\n modalArguments.innerHTML = ''; // Clear previous arguments\r\n\r\n try {\r\n // Fetch both targets and server data simultaneously\r\n const [targetResponse, serverResponse] = await Promise.all([\r\n fetch('/api/targets'),\r\n fetch(`/api/categories/${categoryName}`)\r\n ]);\r\n\r\n if (!targetResponse.ok || !serverResponse.ok) {\r\n throw new Error('Failed to fetch required data');\r\n }\r\n\r\n const [targetData, serverData] = await Promise.all([\r\n targetResponse.json(),\r\n serverResponse.json()\r\n ]);\r\n\r\n if (!targetData.success || !serverData.success) {\r\n throw new Error('Invalid data received');\r\n }\r\n\r\n const mcpServer = serverData.data.feedConfiguration.mcpServers.find(server => server.name === serverName);\r\n if (!mcpServer) {\r\n throw new Error('Server configuration not found');\r\n }\r\n\r\n // Add mode indicator\r\n const modeSpan = document.createElement('span');\r\n modeSpan.textContent = mcpServer.mode || 'stdio';\r\n modeSpan.className = 'ml-2 px-2 py-0.5 text-xs bg-gray-200 text-gray-600 rounded';\r\n title.appendChild(modeSpan);\r\n const installationStatus = serverData.data.installationStatus || {};\r\n const serverStatuses = installationStatus.serversStatus || {};\r\n const serverStatus = serverStatuses[serverName] || { installedStatus: {} };\r\n\r\n await setupModalContent(\r\n targetData,\r\n mcpServer,\r\n serverStatus,\r\n categoryName,\r\n serverName,\r\n serverStatuses,\r\n serverData,\r\n targetDiv,\r\n envInputsDiv,\r\n modalArguments,\r\n modalRequirements\r\n );\r\n\r\n } catch (error) {\r\n console.error(\"Error loading data:\", error);\r\n targetDiv.innerHTML = `<p class=\"text-red-500\">Error: ${error.message}</p>`;\r\n }\r\n\r\n modal.style.display = \"block\";\r\n}\r\n\r\n/**\r\n * Set up the modal content including client list, environment variables, and requirements\r\n * @private\r\n */\r\nasync function setupModalContent(\r\n targetData,\r\n mcpServer,\r\n serverStatus,\r\n categoryName,\r\n serverName,\r\n serverStatuses,\r\n serverData,\r\n targetDiv,\r\n envInputsDiv,\r\n modalArguments,\r\n modalRequirements\r\n) {\r\n // Create client items\r\n setupClientItems(targetData.data, serverStatus, categoryName, serverName, targetDiv);\r\n\r\n // Handle environment variables section\r\n setupEnvironmentVariables(mcpServer, envInputsDiv, targetData);\r\n\r\n // Handle installation arguments section\r\n setupInstallationArguments(mcpServer.installation, modalArguments, categoryName, mcpServer);\r\n\r\n // Handle server requirements section\r\n setupServerRequirements(mcpServer, serverData, categoryName, serverName, modalRequirements);\r\n\r\n // Set up the install form submit handler\r\n const installForm = document.getElementById('installForm');\r\n setupFormSubmitHandler(\r\n installForm,\r\n envInputsDiv,\r\n modalArguments,\r\n modalRequirements,\r\n categoryName,\r\n serverName,\r\n serverStatuses,\r\n serverData,\r\n mcpServer\r\n );\r\n}\r\n\r\n// Make functions available globally\r\nwindow.showInstallModal = showInstallModal;\r\nwindow.closeModal = closeModal;\r\nwindow.uninstallTools = uninstallTools;"],"mappings":"OAASA,UAAWC,gBAAmB,6B","ignoreList":[]}
1
+ {"version":3,"names":["showToast","showConfirm","compareVersions","showInstallLoadingModal","closeModal","setupToggleStyles","handleBulkClientInstall","uninstallTools","setupClientItems","setupEnvironmentVariables","setupInstallationArguments","setupServerRequirements","setupFormSubmitHandler","document","addEventListener","async","showInstallModal","categoryName","serverName","callback","console","log","Promise","resolve","setTimeout","modal","getElementById","error","title","querySelector","envInputsDiv","targetDiv","modalRequirements","modalArguments","window","selectedClients","textContent","innerHTML","targetResponse","serverResponse","all","fetch","ok","Error","targetData","serverData","json","success","mcpServer","data","feedConfiguration","mcpServers","find","server","name","modeSpan","createElement","mode","className","appendChild","serverStatuses","installationStatus","serversStatus","serverStatus","installedStatus","setupModalContent","message","style","display","installation","installForm"],"sources":["0"],"sourcesContent":["import { showToast, showConfirm } from '../notifications.js';\r\nimport { compareVersions } from './versionUtils.js';\r\n// import { delayedAppendInstallLoadingMessage } from './messageQueue.js'; // No longer needed here\r\nimport { showInstallLoadingModal } from './loadingModal.js';\r\nimport { closeModal, setupToggleStyles } from './modalUtils.js';\r\nimport { handleBulkClientInstall, uninstallTools } from './installation.js';\r\nimport {\r\n setupClientItems,\r\n setupEnvironmentVariables,\r\n setupInstallationArguments,\r\n setupServerRequirements,\r\n setupFormSubmitHandler\r\n} from './modalSetup.js';\r\n\r\n// Initialize modal functionality when DOM is loaded\r\ndocument.addEventListener('DOMContentLoaded', () => {\r\n setupToggleStyles();\r\n});\r\n\r\n/**\r\n * Show install modal for MCP tools\r\n * @param {string} categoryName - The category name\r\n * @param {string} serverName - The server name\r\n * @param {Function} callback - Optional callback function\r\n */\r\nexport async function showInstallModal(categoryName, serverName, callback) {\r\n console.log(\"Showing install modal for:\", serverName);\r\n\r\n // Wait for a short delay to ensure modal is loaded\r\n await new Promise(resolve => setTimeout(resolve, 100));\r\n\r\n const modal = document.getElementById('installModal');\r\n if (!modal) {\r\n console.error('Modal container not found');\r\n return;\r\n }\r\n\r\n const title = modal.querySelector('#modalTitle');\r\n const envInputsDiv = modal.querySelector('#modalEnvInputs');\r\n const targetDiv = modal.querySelector('#modalTargets');\r\n const modalRequirements = modal.querySelector('#modalRequirements');\r\n const modalArguments = modal.querySelector('#modalArguments');\r\n\r\n // Global array to track selected clients\r\n window.selectedClients = [];\r\n\r\n // Verify all required modal elements exist\r\n if (!title || !envInputsDiv || !targetDiv || !modalRequirements || !modalArguments) {\r\n console.error('Required modal elements not found');\r\n return;\r\n }\r\n\r\n title.textContent = `Install ${serverName}`;\r\n envInputsDiv.innerHTML = ''; // Clear previous inputs\r\n targetDiv.innerHTML = ''; // Clear previous targets\r\n modalRequirements.innerHTML = ''; // Clear previous requirements\r\n modalArguments.innerHTML = ''; // Clear previous arguments\r\n\r\n try {\r\n // Fetch both targets and server data simultaneously\r\n const [targetResponse, serverResponse] = await Promise.all([\r\n fetch('/api/targets'),\r\n fetch(`/api/categories/${categoryName}`)\r\n ]);\r\n\r\n if (!targetResponse.ok || !serverResponse.ok) {\r\n throw new Error('Failed to fetch required data');\r\n }\r\n\r\n const [targetData, serverData] = await Promise.all([\r\n targetResponse.json(),\r\n serverResponse.json()\r\n ]);\r\n\r\n if (!targetData.success || !serverData.success) {\r\n throw new Error('Invalid data received');\r\n }\r\n\r\n const mcpServer = serverData.data.feedConfiguration.mcpServers.find(server => server.name === serverName);\r\n if (!mcpServer) {\r\n throw new Error('Server configuration not found');\r\n }\r\n\r\n // Add mode indicator\r\n const modeSpan = document.createElement('span');\r\n modeSpan.textContent = mcpServer.mode || 'stdio';\r\n modeSpan.className = 'ml-2 px-2 py-0.5 text-xs bg-gray-200 text-gray-600 rounded';\r\n title.appendChild(modeSpan);\r\n const installationStatus = serverData.data.installationStatus || {};\r\n const serverStatuses = installationStatus.serversStatus || {};\r\n const serverStatus = serverStatuses[serverName] || { installedStatus: {} };\r\n\r\n await setupModalContent(\r\n targetData,\r\n mcpServer,\r\n serverStatus,\r\n categoryName,\r\n serverName,\r\n serverStatuses,\r\n serverData,\r\n targetDiv,\r\n envInputsDiv,\r\n modalArguments,\r\n modalRequirements\r\n );\r\n\r\n } catch (error) {\r\n console.error(\"Error loading data:\", error);\r\n targetDiv.innerHTML = `<p class=\"text-red-500\">Error: ${error.message}</p>`;\r\n }\r\n\r\n modal.style.display = \"block\";\r\n}\r\n\r\n/**\r\n * Set up the modal content including client list, environment variables, and requirements\r\n * @private\r\n */\r\nasync function setupModalContent(\r\n targetData,\r\n mcpServer,\r\n serverStatus,\r\n categoryName,\r\n serverName,\r\n serverStatuses,\r\n serverData,\r\n targetDiv,\r\n envInputsDiv,\r\n modalArguments,\r\n modalRequirements\r\n) {\r\n // Create client items\r\n setupClientItems(targetData.data, serverStatus, categoryName, serverName, targetDiv);\r\n\r\n // Handle environment variables section\r\n setupEnvironmentVariables(mcpServer, envInputsDiv, targetData);\r\n\r\n // Handle installation arguments section\r\n setupInstallationArguments(mcpServer.installation, modalArguments, categoryName, mcpServer);\r\n\r\n // Handle server requirements section\r\n setupServerRequirements(mcpServer, serverData, categoryName, serverName, modalRequirements);\r\n\r\n // Set up the install form submit handler\r\n const installForm = document.getElementById('installForm');\r\n setupFormSubmitHandler(\r\n installForm,\r\n envInputsDiv,\r\n modalArguments,\r\n modalRequirements,\r\n categoryName,\r\n serverName,\r\n serverStatuses,\r\n serverData,\r\n mcpServer\r\n );\r\n}\r\n\r\n// Make functions available globally\r\nwindow.showInstallModal = showInstallModal;\r\nwindow.closeModal = closeModal;\r\nwindow.uninstallTools = uninstallTools;"],"mappings":"OAASA,UAAWC,gBAAmB,6BAC9BC,oBAAuB,2BAEvBC,4BAA+B,2BAC/BC,WAAYC,sBAAyB,yBACrCC,wBAAyBC,mBAAsB,2BAEpDC,iBACAC,0BACAC,2BACAC,wBACAC,2BACG,kBAGPC,SAASC,iBAAiB,oBAAoB,KAC1CT,mBAAmB,WAShBU,eAAeC,iBAAiBC,EAAcC,EAAYC,GAC7DC,QAAQC,IAAI,6BAA8BH,SAGpC,IAAII,SAAQC,GAAWC,WAAWD,EAAS,OAEjD,MAAME,EAAQZ,SAASa,eAAe,gBACtC,IAAKD,EAED,YADAL,QAAQO,MAAM,6BAIlB,MAAMC,EAAQH,EAAMI,cAAc,eAC5BC,EAAeL,EAAMI,cAAc,mBACnCE,EAAYN,EAAMI,cAAc,iBAChCG,EAAoBP,EAAMI,cAAc,sBACxCI,EAAiBR,EAAMI,cAAc,mBAM3C,GAHAK,OAAOC,gBAAkB,GAGpBP,GAAUE,GAAiBC,GAAcC,GAAsBC,EAApE,CAKAL,EAAMQ,YAAc,WAAWlB,IAC/BY,EAAaO,UAAY,GACzBN,EAAUM,UAAY,GACtBL,EAAkBK,UAAY,GAC9BJ,EAAeI,UAAY,GAE3B,IAEI,MAAOC,EAAgBC,SAAwBjB,QAAQkB,IAAI,CACvDC,MAAM,gBACNA,MAAM,mBAAmBxB,OAG7B,IAAKqB,EAAeI,KAAOH,EAAeG,GACtC,MAAM,IAAIC,MAAM,iCAGpB,MAAOC,EAAYC,SAAoBvB,QAAQkB,IAAI,CAC/CF,EAAeQ,OACfP,EAAeO,SAGnB,IAAKF,EAAWG,UAAYF,EAAWE,QACnC,MAAM,IAAIJ,MAAM,yBAGpB,MAAMK,EAAYH,EAAWI,KAAKC,kBAAkBC,WAAWC,MAAKC,GAAUA,EAAOC,OAASpC,IAC9F,IAAK8B,EACD,MAAM,IAAIL,MAAM,kCAIpB,MAAMY,EAAW1C,SAAS2C,cAAc,QACxCD,EAASnB,YAAcY,EAAUS,MAAQ,QACzCF,EAASG,UAAY,6DACrB9B,EAAM+B,YAAYJ,GAClB,MACMK,GADqBf,EAAWI,KAAKY,oBAAsB,CAAC,GACxBC,eAAiB,CAAC,EACtDC,EAAeH,EAAe1C,IAAe,CAAE8C,gBAAiB,CAAC,SAEjEC,kBACFrB,EACAI,EACAe,EACA9C,EACAC,EACA0C,EACAf,EACAd,EACAD,EACAG,EACAD,EAGR,CAAE,MAAOL,GACLP,QAAQO,MAAM,sBAAuBA,GACrCI,EAAUM,UAAY,kCAAkCV,EAAMuC,aAClE,CAEAzC,EAAM0C,MAAMC,QAAU,OA7DtB,MAFIhD,QAAQO,MAAM,oCAgEtB,CAMAZ,eAAekD,kBACXrB,EACAI,EACAe,EACA9C,EACAC,EACA0C,EACAf,EACAd,EACAD,EACAG,EACAD,GAGAxB,iBAAiBoC,EAAWK,KAAMc,EAAc9C,EAAcC,EAAYa,GAG1EtB,0BAA0BuC,EAAWlB,EAAcc,GAGnDlC,2BAA2BsC,EAAUqB,aAAcpC,EAAgBhB,EAAc+B,GAGjFrC,wBAAwBqC,EAAWH,EAAY5B,EAAcC,EAAYc,GAGzE,MAAMsC,EAAczD,SAASa,eAAe,eAC5Cd,uBACI0D,EACAxC,EACAG,EACAD,EACAf,EACAC,EACA0C,EACAf,EACAG,EAER,CAGAd,OAAOlB,iBAAmBA,iBAC1BkB,OAAO9B,WAAaA,WACpB8B,OAAO3B,eAAiBA","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":[],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":["showInstallLoadingModal","updateOverallInstallStatus","addInstallationStep","showToast","async","handleBulkClientInstall","categoryName","serverName","targets","envVars","installingMessage","serverData","serverInstallOptions","console","log","installModal","document","getElementById","style","display","localStorage","setItem","data","installationStatus","serversStatus","serverStatus","installedStatus","length","target","msg","message","delayedAppendInstallLoadingMessage","requestBody","serverList","targetClients","env","response","fetch","method","headers","Accept","body","JSON","stringify","ok","errorData","text","statusText","error","Error","result","json","success","pollNewInstallStatus","interval","completionMessageSent","startTime","Date","now","resp","steps","overallStatus","forEach","step","name","status","timestamp","errorText","Promise","resolve","setTimeout","uninstallTools","selectedTargets","window","selectedClients","Array","isArray","Object","keys","join","toISOString","server","options","removeData","overallStatusTextEl","textContent","startsWith"],"sources":["0"],"sourcesContent":["// import { delayedAppendInstallLoadingMessage } from './messageQueue.js'; // No longer used directly for steps\r\nimport { showInstallLoadingModal, updateOverallInstallStatus, addInstallationStep } from './loadingModal.js';\r\nimport { showToast } from '../notifications.js';\r\n\r\n/**\r\n * Handle bulk client installations\r\n * @param {string} categoryName - The category name\r\n * @param {string} serverName - The server name\r\n * @param {string[]} targets - Target clients\r\n * @param {Object} envVars - Environment variables\r\n * @param {string} installingMessage - Installation message\r\n * @param {Object} serverData - Server data\r\n * @param {Object} serverInstallOptions - Server installation options\r\n */\r\nexport async function handleBulkClientInstall(categoryName, serverName, targets, envVars = {}, installingMessage = \"Starting installation...\", serverData = null, serverInstallOptions = null) {\r\n console.log('[LoadingModal] handleBulkClientInstall called', { categoryName, serverName, targets, envVars, serverInstallOptions });\r\n // Hide install modal, show loading modal\r\n const installModal = document.getElementById('installModal');\r\n console.log('[LoadingModal] installModal:', installModal);\r\n if (installModal) installModal.style.display = \"none\";\r\n\r\n // Store category and server name for potential refresh\r\n if (categoryName) {\r\n localStorage.setItem('lastSelectedCategory', categoryName);\r\n }\r\n if (serverName) {\r\n localStorage.setItem('lastSelectedServerName', serverName);\r\n }\r\n\r\n // If serverData is provided, extract the installing message from it (latest status)\r\n if (serverData?.data?.installationStatus?.serversStatus) {\r\n const serverStatuses = serverData.data.installationStatus.serversStatus;\r\n const serverStatus = serverStatuses[serverName] || { installedStatus: {} };\r\n if (targets?.length > 0) {\r\n const target = targets[0];\r\n const msg = serverStatus.installedStatus?.[target]?.message;\r\n if (msg) installingMessage = msg;\r\n }\r\n // Append the installing message for user visibility\r\n if (installingMessage && installingMessage !== \"Starting installation...\") {\r\n delayedAppendInstallLoadingMessage(installingMessage);\r\n }\r\n }\r\n\r\n showInstallLoadingModal(`Installing ${serverName}`); // Pass initial server name\r\n // delayedAppendInstallLoadingMessage(installingMessage); // Old way\r\n\r\n try {\r\n // delayedAppendInstallLoadingMessage(\"Installing, please wait...\"); // Old way\r\n\r\n // Use serverInstallOptions if provided, otherwise build the traditional options\r\n const requestBody = {\r\n serverList: {\r\n [serverName]: serverInstallOptions || {\r\n targetClients: targets,\r\n env: envVars\r\n }\r\n }\r\n };\r\n\r\n const response = await fetch(`/api/categories/${categoryName}/install`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Accept': 'application/json'\r\n },\r\n body: JSON.stringify(requestBody)\r\n });\r\n\r\n console.log('[LoadingModal] fetch install response:', response);\r\n\r\n if (!response.ok) {\r\n const errorData = await response.text();\r\n // delayedAppendInstallLoadingMessage(`<span style=\"color:#f59e0b;\">Failed: ${errorData || response.statusText}</span>`); // Old way\r\n updateOverallInstallStatus('failed', `Installation request failed: ${errorData || response.statusText}`);\r\n console.error('[LoadingModal] Failed:', errorData || response.statusText);\r\n throw new Error(`Installation failed: ${errorData || response.statusText}`);\r\n }\r\n\r\n const result = await response.json();\r\n console.log('[LoadingModal] install result:', result);\r\n if (!result.success) {\r\n // delayedAppendInstallLoadingMessage(`<span style=\"color:#f59e0b;\">${result.error || 'Operation failed'}</span>`); // Old way\r\n updateOverallInstallStatus('failed', result.error || 'Installation operation failed to start.');\r\n console.error('[LoadingModal] Error:', result.error || 'Operation failed');\r\n throw new Error(result.error || 'Installation failed');\r\n }\r\n\r\n // Start polling for install status\r\n pollNewInstallStatus(categoryName, serverName, 2000); // New polling\r\n } catch (error) {\r\n console.error('[LoadingModal] Error applying configuration:', error);\r\n // delayedAppendInstallLoadingMessage(`<span style=\"color:red;\">Error: ${error.message}</span>`); // Old way\r\n updateOverallInstallStatus('failed', `Error: ${error.message}`);\r\n }\r\n}\r\n\r\n/**\r\n * Poll install status using the new API endpoint for detailed steps.\r\n * @param {string} categoryName - The category name\r\n * @param {string} serverName - The server name\r\n * @param {number} interval - Polling interval in milliseconds\r\n */\r\nasync function pollNewInstallStatus(categoryName, serverName, interval = 2000) {\r\n let completionMessageSent = false;\r\n const startTime = Date.now();\r\n const maxTimeout = 10 * 60 * 1000; // 10 minutes\r\n\r\n while (Date.now() - startTime < maxTimeout && !completionMessageSent) {\r\n try {\r\n const resp = await fetch(`/api/categories/${categoryName}/servers/${serverName}/installation/status`);\r\n if (resp.ok) {\r\n const result = await resp.json();\r\n if (result.success && result.data) {\r\n const { steps, overallStatus, error } = result.data;\r\n\r\n updateOverallInstallStatus(overallStatus, error || `Installation ${overallStatus}`);\r\n\r\n // Always update or insert each step, so UI stays in sync with backend\r\n steps.forEach(step => {\r\n addInstallationStep(step.name, step.message, step.status, step.timestamp);\r\n });\r\n\r\n if (overallStatus === 'completed' || overallStatus === 'failed') {\r\n completionMessageSent = true;\r\n // The hideInstallLoadingModal will be called by the user or after a timeout.\r\n // We can add a \"Close\" button or auto-close logic here if needed.\r\n // For now, just stop polling.\r\n // Ensure final status is displayed\r\n const finalMessage = overallStatus === 'completed' ?\r\n `Installation completed successfully for ${serverName}.` :\r\n `Installation failed for ${serverName}`;\r\n updateOverallInstallStatus(overallStatus, finalMessage);\r\n return;\r\n }\r\n } else if (!result.success && resp.status === 404) {\r\n // Still waiting for the status file to be created.\r\n updateOverallInstallStatus('in-progress', `Waiting for installation to start for ${serverName}...`);\r\n } else if (!result.success) {\r\n updateOverallInstallStatus('failed', `Error fetching status: ${result.error || 'Unknown error'}`);\r\n completionMessageSent = true; // Stop polling on error\r\n return;\r\n }\r\n } else if (resp.status === 404) {\r\n updateOverallInstallStatus('in-progress', `Installation status not yet available for ${serverName}. Retrying...`);\r\n } else {\r\n const errorText = await resp.text();\r\n updateOverallInstallStatus('failed', `Error fetching status: ${errorText || resp.statusText}`);\r\n completionMessageSent = true; // Stop polling on error\r\n return;\r\n }\r\n } catch (error) {\r\n console.error('[LoadingModal] Error polling new install status:', error);\r\n updateOverallInstallStatus('failed', `Error polling status: ${error.message}`);\r\n completionMessageSent = true; // Stop polling on error\r\n return;\r\n }\r\n await new Promise(resolve => setTimeout(resolve, interval));\r\n }\r\n\r\n if (!completionMessageSent) {\r\n updateOverallInstallStatus('failed', `Operation timed out for ${serverName} - Please check logs or refresh.`);\r\n }\r\n}\r\n\r\n/**\r\n * Handle client uninstallation for multiple targets\r\n * @param {string} categoryName - The category name\r\n * @param {Object} serverList - List of servers to uninstall\r\n * @param {string[]} targets - Target clients\r\n */\r\nexport async function uninstallTools(categoryName, serverList, targets) {\r\n\r\n console.log('[LoadingModal] uninstallTools called', { categoryName, serverList, targets });\r\n // Store category and server name for potential refresh\r\n if (categoryName) {\r\n console.log('[LoadingModal] Setting lastSelectedCategory:', categoryName);\r\n localStorage.setItem('lastSelectedCategory', categoryName);\r\n }\r\n\r\n // Get selected targets from window.selectedClients or the provided targets\r\n const selectedTargets = window.selectedClients || (Array.isArray(targets) ? targets : [targets]);\r\n\r\n // Validate selected targets\r\n if (!selectedTargets || selectedTargets.length === 0) {\r\n showToast('Please select at least one client to uninstall.', 'error');\r\n return;\r\n }\r\n\r\n // Ensure showInstallLoadingModal is called if not already visible,\r\n // or ensure overall status is updated if it is.\r\n // For simplicity, we assume the modal is shown by the caller (e.g., modalSetup.js)\r\n // Add uninstall started step\r\n if (typeof addInstallationStep === 'function') {\r\n addInstallationStep(\r\n 'Uninstalling',\r\n `Uninstallation process started for ${Object.keys(serverList).join(', ')}`,\r\n 'in-progress',\r\n new Date().toISOString()\r\n );\r\n }\r\n updateOverallInstallStatus('in-progress', `Starting uninstallation for ${Object.keys(serverList).join(', ')}...`);\r\n\r\n try {\r\n const serverListKeys = Object.keys(serverList);\r\n serverListKeys.forEach(server => {\r\n console.log('[LoadingModal] Setting lastSelectedServerName:', server);\r\n localStorage.setItem('lastSelectedServerName', server);\r\n });\r\n\r\n const response = await fetch(`/api/categories/${categoryName}/uninstall`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n serverList: serverList,\r\n options: {\r\n targets: selectedTargets,\r\n removeData: true\r\n }\r\n })\r\n });\r\n\r\n if (!response.ok) {\r\n const errorData = await response.text();\r\n updateOverallInstallStatus('failed', `Uninstallation request failed: ${errorData || response.statusText}`);\r\n throw new Error(`Uninstallation failed: ${errorData || response.statusText}`);\r\n }\r\n\r\n const result = await response.json();\r\n if (!result.success) {\r\n updateOverallInstallStatus('failed', `Uninstallation failed: ${result.error || 'Operation failed'}`);\r\n throw new Error(result.error || 'Uninstallation failed');\r\n }\r\n\r\n // Add uninstall finished step with 1s delay before updating overall status\r\n if (typeof addInstallationStep === 'function') {\r\n await new Promise(resolve => setTimeout(resolve, 1000));\r\n addInstallationStep(\r\n 'Uninstalling',\r\n `Uninstallation process finished for ${Object.keys(serverList).join(', ')}`,\r\n 'completed',\r\n new Date().toISOString()\r\n );\r\n await new Promise(resolve => setTimeout(resolve, 1000));\r\n addInstallationStep(\r\n 'Uninstall Finished',\r\n `Uninstallation finished for ${Object.keys(serverList).join(', ')}`,\r\n 'completed',\r\n new Date().toISOString()\r\n );\r\n }\r\n await new Promise(resolve => setTimeout(resolve, 1000));\r\n updateOverallInstallStatus('completed', `Successfully uninstalled from ${selectedTargets.join(', ')}.`);\r\n window.selectedClients = []; // Clear selected clients\r\n\r\n } catch (error) {\r\n console.error('Error uninstalling tools:', error);\r\n // The status should have been updated by the specific error handlers above.\r\n // If an error occurs before those, update status here.\r\n const overallStatusTextEl = document.getElementById('overallStatusText'); // Check if element exists\r\n if (overallStatusTextEl && overallStatusTextEl.textContent.startsWith('Starting uninstallation')) {\r\n updateOverallInstallStatus('failed', `Error uninstalling tools: ${error.message}`);\r\n }\r\n showToast(`Error uninstalling tools: ${error.message}`, 'error');\r\n }\r\n}"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":[],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":["ICONS","IN_PROGRESS","COMPLETED","FAILED","CANCELED","overallStatusIconEl","overallStatusTextEl","installStepDetailsListEl","showInstallLoadingModal","initialOperationText","loadingModal","document","getElementById","modalContent","querySelector","innerHTML","closeButton","addEventListener","hideInstallLoadingModal","style","display","console","error","updateOverallInstallStatus","status","message","iconHtml","statusClass","className","textContent","charAt","toUpperCase","slice","addInstallationStep","stepName","timestamp","stepId","replace","listItem","stepIcon","timeString","Date","toLocaleTimeString","hour","minute","second","hour12","e","createElement","id","appendChild","scrollTop","scrollHeight","log","dispatchEvent","CustomEvent"],"sources":["0"],"sourcesContent":["// import { delayedAppendInstallLoadingMessage } from './messageQueue.js'; // Not directly used here anymore for appending\r\n\r\nconst ICONS = {\r\n IN_PROGRESS: '<div class=\"status-spinner\"></div>',\r\n COMPLETED: '<div class=\"status-icon-circled icon-check-container\"><span class=\"status-icon icon-check\">✓</span></div>',\r\n FAILED: '<span class=\"status-icon icon-cross\">✗</span>', // Assuming cross doesn't need a circle for now\r\n CANCELED: '<span class=\"status-icon icon-minus\">–</span>', // Simple minus for canceled\r\n};\r\n\r\nlet overallStatusIconEl, overallStatusTextEl, installStepDetailsListEl;\r\n\r\n/**\r\n * Display the installation loading modal and prepare it for messages.\r\n * @param {string} [initialOperationText='Installing'] - The initial overall operation text.\r\n */\r\nexport function showInstallLoadingModal(initialOperationText = 'Installing') {\r\n const loadingModal = document.getElementById('installLoadingModal');\r\n // Assuming modal-content is the direct child where content should be placed.\r\n const modalContent = loadingModal ? loadingModal.querySelector('.modal-content') : null;\r\n\r\n if (loadingModal && modalContent) {\r\n modalContent.innerHTML = `\r\n <button id=\"installLoadingModalCloseBtn\" class=\"modal-close-btn\">&times;</button>\r\n <div class=\"installation-status-header\">\r\n <div id=\"overallStatusIconContainer\" class=\"overall-status-icon\">\r\n ${ICONS.IN_PROGRESS}\r\n </div>\r\n <h3 id=\"overallStatusText\" class=\"overall-status-text\">${initialOperationText}...</h3>\r\n </div>\r\n <div class=\"installation-steps-container\">\r\n <ul id=\"installStepDetailsList\" class=\"install-step-details-list\"></ul>\r\n </div>\r\n `;\r\n\r\n overallStatusIconEl = modalContent.querySelector('#overallStatusIconContainer');\r\n overallStatusTextEl = modalContent.querySelector('#overallStatusText');\r\n installStepDetailsListEl = modalContent.querySelector('#installStepDetailsList');\r\n\r\n const closeButton = modalContent.querySelector('#installLoadingModalCloseBtn');\r\n if (closeButton) {\r\n closeButton.addEventListener('click', hideInstallLoadingModal);\r\n }\r\n\r\n loadingModal.style.display = 'block';\r\n } else {\r\n console.error('[LoadingModal] Required elements not found: #installLoadingModal or .modal-content');\r\n }\r\n}\r\n\r\n/**\r\n * Updates the overall status display in the loading modal.\r\n * @param {'in-progress' | 'completed' | 'failed'} status - The overall status.\r\n * @param {string} message - The message to display for the overall status.\r\n */\r\nexport function updateOverallInstallStatus(status, message) {\r\n if (!overallStatusIconEl || !overallStatusTextEl) {\r\n // console.warn('[LoadingModal] Overall status elements not initialized or found.');\r\n return;\r\n }\r\n\r\n let iconHtml = ICONS.IN_PROGRESS;\r\n let statusClass = 'in-progress';\r\n\r\n switch (status) {\r\n case 'completed':\r\n iconHtml = ICONS.COMPLETED;\r\n statusClass = 'completed';\r\n break;\r\n case 'failed':\r\n iconHtml = ICONS.FAILED;\r\n statusClass = 'failed';\r\n break;\r\n case 'in-progress':\r\n default:\r\n // iconHtml and statusClass already set\r\n break;\r\n }\r\n\r\n overallStatusIconEl.innerHTML = iconHtml;\r\n overallStatusIconEl.className = `overall-status-icon ${statusClass}`; // Update class for styling\r\n overallStatusTextEl.textContent = message || (status.charAt(0).toUpperCase() + status.slice(1));\r\n}\r\n\r\n/**\r\n * Adds a step to the installation details list.\r\n * @param {string} stepName - The name/title of the step.\r\n * @param {string} message - The message associated with the step.\r\n * @param {'pending'|'in-progress'|'completed'|'failed'|'canceled'} status - The status of the step.\r\n * @param {string} timestamp - ISO timestamp string for the step.\r\n */\r\nexport function addInstallationStep(stepName, message, status, timestamp) {\r\n if (!installStepDetailsListEl) {\r\n return;\r\n }\r\n\r\n // Use stepName as a unique identifier for the step\r\n const stepId = `install-step-${stepName.replace(/[^a-zA-Z0-9_-]/g, '_')}`;\r\n let listItem = document.getElementById(stepId);\r\n\r\n let statusClass = '';\r\n let stepIcon = '';\r\n\r\n switch (status) {\r\n case 'completed':\r\n statusClass = 'success';\r\n stepIcon = '<span class=\"step-icon icon-check-small\">✓</span>';\r\n break;\r\n case 'failed':\r\n statusClass = 'error';\r\n stepIcon = '<span class=\"step-icon icon-cross-small\">✗</span>';\r\n break;\r\n case 'canceled':\r\n statusClass = 'canceled';\r\n stepIcon = '<span class=\"step-icon icon-minus-small\">–</span>';\r\n break;\r\n case 'in-progress':\r\n case 'pending':\r\n default:\r\n statusClass = 'in-progress';\r\n stepIcon = '<div class=\"status-spinner step-spinner\"></div>';\r\n break;\r\n }\r\n\r\n let timeString = '';\r\n if (timestamp) {\r\n try {\r\n timeString = new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });\r\n } catch (e) { /* ignore */ }\r\n }\r\n\r\n const innerHTML = `\r\n ${stepIcon}\r\n <div class=\"step-info\">\r\n <span class=\"step-name\">${stepName}</span>\r\n <span class=\"step-message\">${message || (\r\n status === 'completed' ? 'Completed' :\r\n status === 'failed' ? 'Failed' :\r\n status === 'canceled' ? 'Canceled' :\r\n 'In Progress'\r\n )}</span>\r\n </div>\r\n <span class=\"step-timestamp\">${timeString}</span>\r\n `;\r\n\r\n if (listItem) {\r\n // Update existing step\r\n listItem.className = `step-detail-item ${statusClass}`;\r\n listItem.innerHTML = innerHTML;\r\n } else {\r\n // Create new step\r\n listItem = document.createElement('li');\r\n listItem.id = stepId;\r\n listItem.className = `step-detail-item ${statusClass}`;\r\n listItem.innerHTML = innerHTML;\r\n installStepDetailsListEl.appendChild(listItem);\r\n // Scroll to the bottom of the list\r\n installStepDetailsListEl.scrollTop = installStepDetailsListEl.scrollHeight;\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Hide the install loading modal.\r\n */\r\nexport function hideInstallLoadingModal() {\r\n console.log('[LoadingModal] Hiding installation modal');\r\n const loadingModal = document.getElementById('installLoadingModal');\r\n if (loadingModal) {\r\n loadingModal.style.display = 'none';\r\n\r\n // Clear references to avoid stale elements if modal is reshown\r\n overallStatusIconEl = null;\r\n overallStatusTextEl = null;\r\n installStepDetailsListEl = null;\r\n\r\n // Dispatch an event to signal that the main modal content should be refreshed\r\n document.dispatchEvent(new CustomEvent('refreshMainModalContent'));\r\n console.log('[LoadingModal] Dispatched refreshMainModalContent event.');\r\n } else {\r\n console.error('[LoadingModal] loading modal DOM not found');\r\n }\r\n}"],"mappings":"AAEA,MAAMA,MAAQ,CACVC,YAAa,qCACbC,UAAW,4GACXC,OAAQ,gDACRC,SAAU,iDAGd,IAAIC,oBAAqBC,oBAAqBC,gCAMvC,SAASC,wBAAwBC,EAAuB,cAC3D,MAAMC,EAAeC,SAASC,eAAe,uBAEvCC,EAAeH,EAAeA,EAAaI,cAAc,kBAAoB,KAEnF,GAAIJ,GAAgBG,EAAc,CAC9BA,EAAaE,UAAY,iQAIXf,MAAMC,+GAE6CQ,+MAOjEJ,oBAAsBQ,EAAaC,cAAc,+BACjDR,oBAAsBO,EAAaC,cAAc,sBACjDP,yBAA2BM,EAAaC,cAAc,2BAEtD,MAAME,EAAcH,EAAaC,cAAc,gCAC3CE,GACAA,EAAYC,iBAAiB,QAASC,yBAG1CR,EAAaS,MAAMC,QAAU,OACjC,MACIC,QAAQC,MAAM,qFAEtB,QAOO,SAASC,2BAA2BC,EAAQC,GAC/C,IAAKpB,sBAAwBC,oBAEzB,OAGJ,IAAIoB,EAAW1B,MAAMC,YACjB0B,EAAc,cAElB,OAAQH,GACJ,IAAK,YACDE,EAAW1B,MAAME,UACjByB,EAAc,YACd,MACJ,IAAK,SACDD,EAAW1B,MAAMG,OACjBwB,EAAc,SAQtBtB,oBAAoBU,UAAYW,EAChCrB,oBAAoBuB,UAAY,uBAAuBD,IACvDrB,oBAAoBuB,YAAcJ,GAAYD,EAAOM,OAAO,GAAGC,cAAgBP,EAAOQ,MAAM,EAChG,QASO,SAASC,oBAAoBC,EAAUT,EAASD,EAAQW,GAC3D,IAAK5B,yBACD,OAIJ,MAAM6B,EAAS,gBAAgBF,EAASG,QAAQ,kBAAmB,OACnE,IAAIC,EAAW3B,SAASC,eAAewB,GAEnCT,EAAc,GACdY,EAAW,GAEf,OAAQf,GACJ,IAAK,YACDG,EAAc,UACdY,EAAW,oDACX,MACJ,IAAK,SACDZ,EAAc,QACdY,EAAW,oDACX,MACJ,IAAK,WACDZ,EAAc,WACdY,EAAW,oDACX,MAGJ,QACIZ,EAAc,cACdY,EAAW,kDAInB,IAAIC,EAAa,GACjB,GAAIL,EACA,IACIK,EAAa,IAAIC,KAAKN,GAAWO,mBAAmB,GAAI,CAAEC,KAAM,UAAWC,OAAQ,UAAWC,OAAQ,UAAWC,QAAQ,GAC7H,CAAE,MAAOC,GAAkB,CAG/B,MAAMhC,EAAY,aACZwB,2EAE4BL,oDACGT,IACd,cAAXD,EAAyB,YACd,WAAXA,EAAsB,SACX,aAAXA,EAAwB,WACxB,+EAGuBgB,iBAG/BF,GAEAA,EAASV,UAAY,oBAAoBD,IACzCW,EAASvB,UAAYA,IAGrBuB,EAAW3B,SAASqC,cAAc,MAClCV,EAASW,GAAKb,EACdE,EAASV,UAAY,oBAAoBD,IACzCW,EAASvB,UAAYA,EACrBR,yBAAyB2C,YAAYZ,GAErC/B,yBAAyB4C,UAAY5C,yBAAyB6C,aAEtE,QAMO,SAASlC,0BACZG,QAAQgC,IAAI,4CACZ,MAAM3C,EAAeC,SAASC,eAAe,uBACzCF,GACAA,EAAaS,MAAMC,QAAU,OAG7Bf,oBAAsB,KACtBC,oBAAsB,KACtBC,yBAA2B,KAG3BI,SAAS2C,cAAc,IAAIC,YAAY,4BACvClC,QAAQgC,IAAI,6DAEZhC,QAAQC,MAAM,6CAEtB","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["showToast","showConfirm"],"sources":["0"],"sourcesContent":["import { showToast, showConfirm } from '../notifications.js';\r\nimport { showInstallLoadingModal, updateOverallInstallStatus } from './loadingModal.js';\r\n// import { delayedAppendInstallLoadingMessage } from './messageQueue.js'; // No longer used\r\nimport { uninstallTools } from './installation.js';\r\nimport { handleBulkClientInstall } from './installation.js';\r\nimport { compareVersions } from './versionUtils.js';\r\n\r\n/**\r\n * Set up client items in the modal\r\n */\r\nexport function setupClientItems(targets, serverStatus, categoryName, serverName, targetDiv) {\r\n targets.forEach(target => {\r\n const operationStatus = serverStatus.installedStatus[target] || { status: 'not-installed', type: 'check', target: 'server' };\r\n\r\n // Determine client status\r\n let statusText = '';\r\n let statusClass = '';\r\n\r\n if (operationStatus.status === 'completed' && operationStatus.type === 'install') {\r\n statusText = 'Installed';\r\n statusClass = 'installed';\r\n } else if (operationStatus.status === 'pending') {\r\n statusText = 'Pending Requirements';\r\n statusClass = 'pending';\r\n } else if (operationStatus.status === 'in-progress') {\r\n statusText = 'In Progress';\r\n statusClass = 'pending';\r\n } else if (operationStatus.status === 'failed') {\r\n statusText = 'Failed';\r\n statusClass = 'not-installed';\r\n }\r\n\r\n const isConfigured = operationStatus.status === 'completed' && operationStatus.type === 'install';\r\n const isInProgress = operationStatus.status === 'in-progress';\r\n const isSelectable = !isConfigured && !isInProgress;\r\n\r\n const clientItem = createClientItem(target, statusText, statusClass, isSelectable, isConfigured, isInProgress);\r\n setupClientItemListeners(clientItem, target, isSelectable);\r\n setupUninstallButton(clientItem, statusText, operationStatus, target, categoryName, serverName, statusClass);\r\n targetDiv.appendChild(clientItem);\r\n });\r\n\r\n addSectionTitle(targetDiv);\r\n}\r\n\r\n/**\r\n * Set up environment variables section in the modal\r\n */\r\nexport function setupEnvironmentVariables(mcpServer, envInputsDiv, targetData) {\r\n // For SSE mode, don't show env section at all\r\n if (mcpServer.mode === 'sse' || mcpServer.mode === 'streamable-http') {\r\n envInputsDiv.style.display = 'none';\r\n return;\r\n }\r\n\r\n const envRequirements = mcpServer.installation?.['env'] || mcpServer.installation?.env || {};\r\n addEnvironmentTitle(envInputsDiv);\r\n\r\n // Fetch system settings to get userConfigurations for env defaults\r\n fetch('/api/settings')\r\n .then(response => response.json())\r\n .then(data => {\r\n const userConfigurations = (data && data.data && data.data.userConfigurations) ? data.data.userConfigurations : {};\r\n if (Object.keys(envRequirements).length === 0) {\r\n addNoEnvMessage(envInputsDiv);\r\n } else {\r\n createEnvironmentInputs(envRequirements, envInputsDiv, targetData.clientMcpSettings, mcpServer.name, userConfigurations);\r\n }\r\n })\r\n .catch(() => {\r\n // fallback to old logic if fetch fails\r\n if (Object.keys(envRequirements).length === 0) {\r\n addNoEnvMessage(envInputsDiv);\r\n } else {\r\n createEnvironmentInputs(envRequirements, envInputsDiv, targetData.clientMcpSettings, mcpServer.name, {});\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Set up installation arguments section in the modal\r\n */\r\nexport function setupInstallationArguments(installation, modalArguments, categoryName, mcpServer) {\r\n if (!installation) {\r\n modalArguments.style.display = 'none';\r\n return;\r\n }\r\n\r\n if (mcpServer?.mode === 'sse' || mcpServer?.mode === 'streamable-http') {\r\n // For SSE mode, show URL and Starting Command if available\r\n modalArguments.innerHTML = ''; // Clear existing content\r\n let sseInfoAdded = false;\r\n if (installation.url) {\r\n addArgumentsTitle(modalArguments); // Add title only if there's content\r\n createReadOnlyTextBox(modalArguments, 'Server URL', installation.url, 'sse_url');\r\n sseInfoAdded = true;\r\n }\r\n if (installation.startingCommand) {\r\n if (!sseInfoAdded) addArgumentsTitle(modalArguments); // Add title if not already added\r\n createReadOnlyTextBox(modalArguments, 'Starting Command', installation.startingCommand, 'sse_starting_command');\r\n sseInfoAdded = true;\r\n }\r\n if (!sseInfoAdded) {\r\n modalArguments.style.display = 'none'; // Hide if no SSE info to show\r\n }\r\n return;\r\n }\r\n\r\n // For non-SSE modes, keep the existing logic\r\n addArgumentsTitle(modalArguments);\r\n const container = createArgumentsContainer();\r\n modalArguments.appendChild(container);\r\n\r\n if (installation.args && Array.isArray(installation.args)) {\r\n installation.args.forEach(arg => {\r\n container.appendChild(createArgumentInput(arg));\r\n });\r\n } else {\r\n container.appendChild(createArgumentInput());\r\n }\r\n\r\n if (installation.command === 'python' || installation.command.includes('python')) {\r\n addPythonEnvironmentInput(categoryName, mcpServer.name, modalArguments);\r\n }\r\n}\r\n\r\n/**\r\n * Set up server requirements section in the modal\r\n */\r\nexport function setupServerRequirements(mcpServer, serverData, categoryName, serverName, modalRequirements) {\r\n // For SSE mode, don't show requirements section at all\r\n if (mcpServer.mode === 'sse' || mcpServer.mode === 'streamable-http') {\r\n modalRequirements.style.display = 'none';\r\n return;\r\n }\r\n\r\n const serverRequirements = mcpServer.dependencies?.requirements || [];\r\n const requirements = serverData.data.installationStatus?.requirementsStatus || {};\r\n\r\n if (serverRequirements.length > 0) {\r\n const reqHtml = renderRequirements(serverRequirements, requirements, categoryName, serverName);\r\n modalRequirements.innerHTML = `\r\n <h3 class=\"text-lg font-semibold text-gray-700 mb-3\">Dependencies</h3>\r\n <p class=\"text-sm text-gray-600 mb-4\">These dependencies will be automatically installed when installing the server</p>\r\n ${reqHtml}\r\n `;\r\n\r\n setupUpdateToggles(modalRequirements);\r\n } else {\r\n modalRequirements.innerHTML = '<p class=\"text-gray-600\">No additional dependencies required.</p>';\r\n }\r\n}\r\n\r\n/**\r\n * Set up the form submit handler\r\n */\r\nexport function setupFormSubmitHandler(form, envInputsDiv, modalArguments, modalRequirements, categoryName, serverName, serverStatuses, serverData, mcpServer) {\r\n form.onsubmit = (e) => {\r\n e.preventDefault();\r\n\r\n const envVars = {};\r\n const args = [];\r\n const requirementsToUpdate = [];\r\n let pythonEnv = undefined;\r\n\r\n // Only collect env, args and requirements if not in SSE mode\r\n if (mcpServer?.mode !== 'sse' && mcpServer?.mode !== 'streamable-http') {\r\n const inputs = envInputsDiv.querySelectorAll('input');\r\n inputs.forEach(input => {\r\n if (input.name && input.value) {\r\n envVars[input.name] = input.value;\r\n }\r\n });\r\n\r\n const argInputs = modalArguments.querySelectorAll('.arg-input');\r\n args.push(...Array.from(argInputs)\r\n .map(input => input.value.trim())\r\n .filter(val => val !== ''));\r\n\r\n const pythonEnvInput = document.getElementById('python_env');\r\n pythonEnv = pythonEnvInput?.value?.trim();\r\n\r\n const updateToggles = modalRequirements.querySelectorAll('.toggle-update:checked');\r\n updateToggles.forEach(toggle => {\r\n requirementsToUpdate.push({\r\n name: toggle.dataset.name,\r\n version: toggle.dataset.version\r\n });\r\n });\r\n }\r\n\r\n // Only POST userConfigurations and/or pythonEnvs if there is a difference, then proceed with install\r\n fetch('/api/settings')\r\n .then(response => response.json())\r\n .then(data => {\r\n const userConfigurations = (data && data.data && data.data.userConfigurations) ? data.data.userConfigurations : {};\r\n const pythonEnvs = (data && data.data && data.data.pythonEnvs) ? data.data.pythonEnvs : {};\r\n const updatedUserConfigurations = { ...userConfigurations };\r\n const updatedPythonEnvs = { ...pythonEnvs };\r\n\r\n Object.keys(envVars).forEach(key => {\r\n updatedUserConfigurations[key] = envVars[key];\r\n });\r\n\r\n // Check for userConfigurations difference\r\n let needsUserConfigUpdate = false;\r\n const allUserConfigKeys = new Set([...Object.keys(userConfigurations), ...Object.keys(envVars)]);\r\n for (const key of allUserConfigKeys) {\r\n if (userConfigurations[key] !== updatedUserConfigurations[key]) {\r\n needsUserConfigUpdate = true;\r\n break;\r\n }\r\n }\r\n\r\n // Check for pythonEnvs difference\r\n let needsPythonEnvUpdate = false;\r\n if (pythonEnv !== undefined && pythonEnv !== pythonEnvs[`${categoryName}:${serverName}`]) {\r\n updatedPythonEnvs[`${categoryName}:${serverName}`] = pythonEnv;\r\n needsPythonEnvUpdate = true;\r\n }\r\n\r\n // If either needs update, POST the merged object\r\n if (needsUserConfigUpdate || needsPythonEnvUpdate) {\r\n const postBody = {};\r\n if (needsUserConfigUpdate) postBody.userConfigurations = updatedUserConfigurations;\r\n if (needsPythonEnvUpdate) postBody.pythonEnvs = updatedPythonEnvs;\r\n fetch('/api/settings', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(postBody)\r\n }).then(() => {\r\n proceedInstall();\r\n }).catch(() => {\r\n proceedInstall();\r\n });\r\n } else {\r\n proceedInstall();\r\n }\r\n })\r\n .catch(() => {\r\n proceedInstall();\r\n });\r\n\r\n function proceedInstall() {\r\n const hasRequirementsToUpdate = requirementsToUpdate.length > 0;\r\n\r\n const uninstallBtn = document.querySelector('.uninstall-btn');\r\n if (!uninstallBtn || !uninstallBtn.matches(':active')) {\r\n const selectedTargets = window.selectedClients;\r\n if (selectedTargets.length === 0 && !hasRequirementsToUpdate) {\r\n showToast('Please select at least one client to configure.', 'error');\r\n return;\r\n }\r\n\r\n let installingMessage = \"Starting installation...\";\r\n const serverStatus = serverStatuses[serverName] || { installedStatus: {} };\r\n if (selectedTargets.length > 0) {\r\n const target = selectedTargets[0];\r\n const msg = serverStatus.installedStatus?.[target]?.message;\r\n if (msg) installingMessage = msg;\r\n }\r\n\r\n const serverInstallOptions = {\r\n targetClients: selectedTargets,\r\n env: envVars,\r\n args: args,\r\n settings: pythonEnv ? { pythonEnv: pythonEnv } : {}\r\n };\r\n\r\n if (requirementsToUpdate.length > 0) {\r\n serverInstallOptions.requirements = requirementsToUpdate;\r\n }\r\n\r\n handleBulkClientInstall(categoryName, serverName, selectedTargets, envVars, installingMessage, serverData, serverInstallOptions);\r\n }\r\n }\r\n };\r\n}\r\n\r\n// Helper functions\r\nfunction createClientItem(target, statusText, statusClass, isSelectable, isConfigured, isInProgress) {\r\n const clientItem = document.createElement('div');\r\n clientItem.className = 'client-item';\r\n clientItem.dataset.target = target;\r\n clientItem.dataset.selected = 'false';\r\n\r\n // Create client info (name)\r\n const clientInfo = document.createElement('div');\r\n clientInfo.className = 'client-info';\r\n\r\n // Client name label\r\n const clientName = document.createElement('span');\r\n clientName.className = 'text-sm font-medium text-gray-900';\r\n clientName.textContent = target;\r\n\r\n // Add elements to client info\r\n clientInfo.appendChild(clientName);\r\n\r\n // Add client info to client item\r\n clientItem.appendChild(clientInfo);\r\n\r\n if (!isSelectable) {\r\n clientItem.classList.add('non-selectable');\r\n if (isConfigured) {\r\n clientItem.classList.add('installed-item');\r\n clientItem.title = 'Already installed';\r\n } else if (isInProgress) {\r\n clientItem.classList.add('in-progress-item');\r\n clientItem.title = 'Installation in progress';\r\n }\r\n }\r\n\r\n return clientItem;\r\n}\r\n\r\nfunction setupClientItemListeners(clientItem, target, isSelectable) {\r\n if (isSelectable) {\r\n clientItem.addEventListener('click', () => {\r\n const isSelected = clientItem.dataset.selected === 'true';\r\n clientItem.dataset.selected = isSelected ? 'false' : 'true';\r\n\r\n if (isSelected) {\r\n clientItem.classList.remove('selected');\r\n window.selectedClients = window.selectedClients.filter(c => c !== target);\r\n } else {\r\n clientItem.classList.add('selected');\r\n if (!window.selectedClients.includes(target)) {\r\n window.selectedClients.push(target);\r\n }\r\n }\r\n });\r\n }\r\n}\r\n\r\nfunction setupUninstallButton(clientItem, statusText, operationStatus, target, categoryName, serverName, statusClass) {\r\n if (statusText) {\r\n const statusContainer = document.createElement('div');\r\n statusContainer.className = 'status-container';\r\n\r\n const statusBadge = document.createElement('span');\r\n statusBadge.className = `status-badge ${statusClass}`;\r\n statusBadge.textContent = statusText;\r\n statusContainer.appendChild(statusBadge);\r\n\r\n if (operationStatus.status === 'completed' && operationStatus.type === 'install') {\r\n const uninstallBtn = createUninstallButton(target, categoryName, serverName);\r\n statusContainer.appendChild(uninstallBtn);\r\n }\r\n\r\n clientItem.appendChild(statusContainer);\r\n }\r\n}\r\n\r\nfunction createUninstallButton(target, categoryName, serverName) {\r\n const uninstallBtn = document.createElement('button');\r\n uninstallBtn.className = 'uninstall-btn text-red-600 hover:text-red-800 ml-2';\r\n uninstallBtn.innerHTML = '<i class=\"bx bx-trash\"></i>';\r\n uninstallBtn.title = 'Uninstall from this client';\r\n\r\n uninstallBtn.onclick = async (e) => {\r\n e.stopPropagation();\r\n e.preventDefault();\r\n\r\n const confirmed = await showConfirm('Uninstall Confirmation',\r\n `Are you sure you want to uninstall ${serverName} from ${target}?`);\r\n\r\n if (confirmed) {\r\n window.selectedClients = [target];\r\n showInstallLoadingModal('Uninstalling');\r\n const serverList = {\r\n [serverName]: {\r\n removeData: true\r\n }\r\n };\r\n\r\n try {\r\n updateOverallInstallStatus('in-progress', `Uninstalling ${serverName} from ${target}...`);\r\n await uninstallTools(categoryName, serverList, [target]);\r\n // uninstallTools will now handle its own final status update via updateOverallInstallStatus\r\n } catch (error) {\r\n updateOverallInstallStatus('failed', `Error during uninstall: ${error.message}`);\r\n // No need to throw here if uninstallTools handles its errors by updating status\r\n }\r\n }\r\n return false;\r\n };\r\n\r\n return uninstallBtn;\r\n}\r\n\r\nfunction addSectionTitle(targetDiv) {\r\n if (!targetDiv.querySelector('.section-title')) {\r\n const titleElement = document.createElement('h3');\r\n titleElement.className = 'section-title text-lg font-semibold text-gray-700 mb-3';\r\n titleElement.textContent = 'Client Status';\r\n targetDiv.insertBefore(titleElement, targetDiv.firstChild);\r\n }\r\n}\r\n\r\nfunction addEnvironmentTitle(envInputsDiv) {\r\n if (!envInputsDiv.querySelector('.section-title')) {\r\n const envTitle = document.createElement('h3');\r\n envTitle.className = 'section-title text-lg font-semibold text-gray-700 mb-3';\r\n envTitle.textContent = 'Environment Variables';\r\n envInputsDiv.insertBefore(envTitle, envInputsDiv.firstChild);\r\n }\r\n}\r\n\r\nfunction addNoEnvMessage(envInputsDiv) {\r\n const noEnvMessage = document.createElement('p');\r\n noEnvMessage.className = 'text-gray-600';\r\n noEnvMessage.textContent = 'No environment variables required for this MCP server.';\r\n envInputsDiv.appendChild(noEnvMessage);\r\n}\r\n\r\nfunction createEnvironmentInputs(envRequirements, envInputsDiv, clientSettings, serverName, userConfigurations = {}) {\r\n Object.keys(envRequirements).forEach(key => {\r\n const req = envRequirements[key];\r\n const inputId = `env_${key}`;\r\n const inputWrapper = document.createElement('div');\r\n inputWrapper.className = 'mb-3';\r\n\r\n const label = document.createElement('label');\r\n label.htmlFor = inputId;\r\n label.className = 'block text-sm font-medium text-gray-700 mb-1';\r\n label.innerHTML = `${key} ${req.Required ? '<span class=\"text-red-500\">*</span>' : ''}`;\r\n\r\n const input = document.createElement('input');\r\n input.type = req.isSecret ? 'password' : 'text';\r\n input.id = inputId;\r\n input.name = key;\r\n input.placeholder = req.Description || key;\r\n input.required = req.Required;\r\n input.className = 'input-field';\r\n\r\n // For default value, first check userConfigurations, then client settings, then provided default\r\n let defaultValue = req.Default || '';\r\n\r\n // Use userConfigurations if present\r\n if (userConfigurations && userConfigurations[key]) {\r\n defaultValue = userConfigurations[key];\r\n } else if (clientSettings && clientSettings.MSRooCode &&\r\n clientSettings.MSRooCode.mcpServers &&\r\n clientSettings.MSRooCode.mcpServers[serverName] &&\r\n clientSettings.MSRooCode.mcpServers[serverName].env &&\r\n clientSettings.MSRooCode.mcpServers[serverName].env[key]) {\r\n defaultValue = clientSettings.MSRooCode.mcpServers[serverName].env[key];\r\n }\r\n\r\n input.value = defaultValue;\r\n\r\n inputWrapper.appendChild(label);\r\n inputWrapper.appendChild(input);\r\n\r\n if (req.Description) {\r\n const description = document.createElement('p');\r\n description.className = 'text-xs text-gray-500 mt-1';\r\n description.textContent = req.Description;\r\n inputWrapper.appendChild(description);\r\n }\r\n\r\n envInputsDiv.appendChild(inputWrapper);\r\n });\r\n}\r\n\r\nfunction addArgumentsTitle(modalArguments) {\r\n const argsTitle = document.createElement('h3');\r\n argsTitle.className = 'section-title text-lg font-semibold text-gray-700 mb-3';\r\n argsTitle.textContent = 'Arguments';\r\n modalArguments.appendChild(argsTitle);\r\n}\r\n\r\nfunction createArgumentsContainer() {\r\n const container = document.createElement('div');\r\n container.className = 'args-container mb-3';\r\n\r\n const addButton = document.createElement('button');\r\n addButton.type = 'button';\r\n addButton.className = 'add-arg-button px-3 py-1 text-sm text-blue-600 hover:text-blue-800 border border-blue-600 hover:border-blue-800 rounded-md mb-2';\r\n addButton.innerHTML = '<i class=\"bx bx-plus\"></i> Add Argument';\r\n addButton.onclick = () => container.appendChild(createArgumentInput());\r\n\r\n container.appendChild(addButton);\r\n return container;\r\n}\r\n\r\nfunction createArgumentInput(value = '') {\r\n const argWrapper = document.createElement('div');\r\n argWrapper.className = 'arg-wrapper flex items-center gap-2 mb-2';\r\n\r\n const input = document.createElement('input');\r\n input.type = 'text';\r\n input.className = 'arg-input flex-grow';\r\n input.value = value;\r\n input.placeholder = 'Enter argument value';\r\n\r\n const removeButton = document.createElement('button');\r\n removeButton.type = 'button';\r\n removeButton.className = 'remove-arg-button text-red-600 hover:text-red-800';\r\n removeButton.innerHTML = '<i class=\"bx bx-trash\"></i>';\r\n removeButton.onclick = () => argWrapper.remove();\r\n\r\n argWrapper.appendChild(input);\r\n argWrapper.appendChild(removeButton);\r\n return argWrapper;\r\n}\r\n\r\n/**\r\n * Creates a read-only text box with a label, typically for displaying SSE server info.\r\n * Appends the created element to the parentElement.\r\n */\r\nfunction createReadOnlyTextBox(parentElement, labelText, value, id) {\r\n const wrapper = document.createElement('div');\r\n wrapper.className = 'mb-3 sse-info-field'; // Added for styling consistency\r\n\r\n const label = document.createElement('label');\r\n label.htmlFor = id;\r\n label.className = 'block text-sm font-medium text-gray-700 mb-1';\r\n label.textContent = labelText;\r\n\r\n const textBox = document.createElement('input');\r\n textBox.type = 'text';\r\n textBox.id = id;\r\n textBox.value = value;\r\n textBox.readOnly = true;\r\n textBox.className = 'input-field sse-input-readonly w-full'; // Reuses 'input-field' and adds specific class for styling, w-full for width\r\n\r\n wrapper.appendChild(label);\r\n wrapper.appendChild(textBox);\r\n parentElement.appendChild(wrapper); // Append directly to the provided parent\r\n}\r\nfunction addPythonEnvironmentInput(categoryName, serverName, modalArguments) {\r\n const pythonEnvWrapper = document.createElement('div');\r\n pythonEnvWrapper.className = 'mt-4';\r\n\r\n const pythonEnvLabel = document.createElement('label');\r\n pythonEnvLabel.htmlFor = 'python_env';\r\n pythonEnvLabel.className = 'block text-sm font-medium text-gray-700 mb-1';\r\n pythonEnvLabel.textContent = 'Python Environment';\r\n\r\n const pythonEnvInput = document.createElement('input');\r\n pythonEnvInput.type = 'text';\r\n pythonEnvInput.id = 'python_env';\r\n pythonEnvInput.className = 'input-field';\r\n pythonEnvInput.placeholder = 'Enter Python environment path (optional)';\r\n\r\n const envDescription = document.createElement('p');\r\n envDescription.className = 'text-xs text-gray-500 mt-1';\r\n envDescription.textContent = 'Specify the Python exectable file(e.g. C:/python312/python) to use for installation. Leave empty to use system default. You can specify value in Settings page';\r\n\r\n pythonEnvWrapper.appendChild(pythonEnvLabel);\r\n pythonEnvWrapper.appendChild(pythonEnvInput);\r\n pythonEnvWrapper.appendChild(envDescription);\r\n modalArguments.appendChild(pythonEnvWrapper);\r\n // Fetch system settings and set default value for python_env\r\n fetch('/api/settings')\r\n .then(response => response.json())\r\n .then(data => {\r\n if (data && data.data && data.data.pythonEnvs) {\r\n pythonEnvInput.value = data.data.pythonEnvs[`${categoryName}:${serverName}`] || data.data.pythonEnvs['system'] || '';\r\n }\r\n })\r\n .catch(() => {\r\n // Ignore errors, leave input empty\r\n });\r\n}\r\n\r\nfunction renderRequirements(serverRequirements, requirements, categoryName, serverName) {\r\n return serverRequirements.map(req => {\r\n const status = requirements[req.name] || {};\r\n let statusClass = status.installed\r\n ? 'text-green-600 bg-green-50'\r\n : 'text-yellow-600 bg-yellow-50';\r\n let statusText = status.installed ? 'Installed' : 'Required';\r\n let versionDisplay = status.version ? ` • <span class=\"font-medium\">${status.version}</span>` : '';\r\n let updateToggle = '';\r\n\r\n // Check if there's an available update\r\n if (status.installed && status.availableUpdate && status.availableUpdate.version) {\r\n if (status.version && compareVersions(status.availableUpdate.version, status.version) > 0) {\r\n // Show version update information with yellow color and icon\r\n statusClass = 'text-yellow-600 bg-yellow-50';\r\n statusText = `<span style=\"color: #f59e0b; font-weight: bold; margin-right: 5px;\">↑</span>${status.availableUpdate.version}`;\r\n\r\n // Create a toggle switch for update\r\n updateToggle = `\r\n <label class=\"inline-flex items-center cursor-pointer ml-2\">\r\n <input type=\"checkbox\" class=\"toggle-update sr-only\"\r\n data-name=\"${req.name}\"\r\n data-version=\"${status.availableUpdate.version}\"\r\n data-category=\"${categoryName}\"\r\n data-server=\"${serverName}\">\r\n <div class=\"relative w-10 h-5 bg-gray-200 rounded-full toggle-bg\">\r\n <div class=\"absolute inset-y-0 left-0 w-5 h-5 bg-white rounded-full transition-transform duration-300 transform\"></div>\r\n </div>\r\n <span class=\"ml-2 text-sm text-gray-700\">Update</span>\r\n </label>\r\n `;\r\n }\r\n }\r\n\r\n return `\r\n <div class=\"border border-gray-200 p-3 rounded-lg mb-2 hover:bg-gray-50\">\r\n <div class=\"flex justify-between items-center\">\r\n <div>\r\n <div class=\"font-semibold text-gray-800\">${req.name}</div>\r\n <div class=\"text-sm text-gray-600 shadow-sm p-1 rounded bg-gray-50\">\r\n <span class=\"font-medium\">${status.type || 'package'}</span>${versionDisplay}\r\n </div>\r\n </div>\r\n <div class=\"flex items-center\">\r\n <span class=\"${statusClass} inline-flex items-center px-3 py-1 rounded-full text-sm\">\r\n ${statusText}\r\n </span>\r\n ${updateToggle}\r\n </div>\r\n </div>\r\n </div>\r\n `;\r\n }).join('');\r\n}\r\n\r\nfunction setupUpdateToggles(modalRequirements) {\r\n const updateToggles = modalRequirements.querySelectorAll('.toggle-update');\r\n updateToggles.forEach(toggle => {\r\n toggle.addEventListener('change', function () {\r\n const toggleBg = this.parentElement.querySelector('.toggle-bg');\r\n if (this.checked) {\r\n toggleBg.classList.add('bg-blue-500');\r\n toggleBg.querySelector('div').classList.add('translate-x-5');\r\n } else {\r\n toggleBg.classList.remove('bg-blue-500');\r\n toggleBg.querySelector('div').classList.remove('translate-x-5');\r\n }\r\n });\r\n });\r\n}"],"mappings":"OAASA,UAAWC,gBAAmB,6B","ignoreList":[]}
1
+ {"version":3,"names":["showToast","showConfirm","showInstallLoadingModal","updateOverallInstallStatus","uninstallTools","handleBulkClientInstall","compareVersions","setupClientItems","targets","serverStatus","categoryName","serverName","targetDiv","forEach","target","operationStatus","installedStatus","status","type","statusText","statusClass","isConfigured","isInProgress","isSelectable","clientItem","createClientItem","setupClientItemListeners","setupUninstallButton","appendChild","addSectionTitle","setupEnvironmentVariables","mcpServer","envInputsDiv","targetData","mode","style","display","envRequirements","installation","env","addEnvironmentTitle","fetch","then","response","json","data","userConfigurations","Object","keys","length","addNoEnvMessage","createEnvironmentInputs","clientMcpSettings","name","catch","setupInstallationArguments","modalArguments","innerHTML","sseInfoAdded","url","addArgumentsTitle","createReadOnlyTextBox","startingCommand","container","createArgumentsContainer","args","Array","isArray","arg","createArgumentInput","command","includes","addPythonEnvironmentInput","setupServerRequirements","serverData","modalRequirements","serverRequirements","dependencies","requirements","installationStatus","requirementsStatus","reqHtml","renderRequirements","setupUpdateToggles","setupFormSubmitHandler","form","serverStatuses","onsubmit","e","preventDefault","envVars","requirementsToUpdate","pythonEnv","querySelectorAll","input","value","argInputs","push","from","map","trim","filter","val","pythonEnvInput","document","getElementById","toggle","dataset","version","proceedInstall","hasRequirementsToUpdate","uninstallBtn","querySelector","matches","selectedTargets","window","selectedClients","installingMessage","msg","message","serverInstallOptions","targetClients","settings","pythonEnvs","updatedUserConfigurations","updatedPythonEnvs","key","needsUserConfigUpdate","allUserConfigKeys","Set","needsPythonEnvUpdate","undefined","postBody","method","headers","body","JSON","stringify","createElement","className","selected","clientInfo","clientName","textContent","classList","add","title","addEventListener","isSelected","remove","c","statusContainer","statusBadge","createUninstallButton","onclick","async","stopPropagation","serverList","removeData","error","titleElement","insertBefore","firstChild","envTitle","noEnvMessage","getClientEnvValue","clientSettings","clientNames","mcpServers","req","inputId","inputWrapper","label","htmlFor","Required","isSecret","id","placeholder","Description","required","defaultValue","Default","storedValue","description","argsTitle","addButton","argWrapper","removeButton","parentElement","labelText","wrapper","textBox","readOnly","pythonEnvWrapper","pythonEnvLabel","envDescription","installed","versionDisplay","updateToggle","availableUpdate","join","toggleBg","this","checked"],"sources":["0"],"sourcesContent":["import { showToast, showConfirm } from '../notifications.js';\r\nimport { showInstallLoadingModal, updateOverallInstallStatus } from './loadingModal.js';\r\n// import { delayedAppendInstallLoadingMessage } from './messageQueue.js'; // No longer used\r\nimport { uninstallTools } from './installation.js';\r\nimport { handleBulkClientInstall } from './installation.js';\r\nimport { compareVersions } from './versionUtils.js';\r\n\r\n/**\r\n * Set up client items in the modal\r\n */\r\nexport function setupClientItems(targets, serverStatus, categoryName, serverName, targetDiv) {\r\n targets.forEach(target => {\r\n const operationStatus = serverStatus.installedStatus[target] || { status: 'not-installed', type: 'check', target: 'server' };\r\n\r\n // Determine client status\r\n let statusText = '';\r\n let statusClass = '';\r\n\r\n if (operationStatus.status === 'completed' && operationStatus.type === 'install') {\r\n statusText = 'Installed';\r\n statusClass = 'installed';\r\n } else if (operationStatus.status === 'pending') {\r\n statusText = 'Pending Requirements';\r\n statusClass = 'pending';\r\n } else if (operationStatus.status === 'in-progress') {\r\n statusText = 'In Progress';\r\n statusClass = 'pending';\r\n } else if (operationStatus.status === 'failed') {\r\n statusText = 'Failed';\r\n statusClass = 'not-installed';\r\n }\r\n\r\n const isConfigured = operationStatus.status === 'completed' && operationStatus.type === 'install';\r\n const isInProgress = operationStatus.status === 'in-progress';\r\n const isSelectable = !isConfigured && !isInProgress;\r\n\r\n const clientItem = createClientItem(target, statusText, statusClass, isSelectable, isConfigured, isInProgress);\r\n setupClientItemListeners(clientItem, target, isSelectable);\r\n setupUninstallButton(clientItem, statusText, operationStatus, target, categoryName, serverName, statusClass);\r\n targetDiv.appendChild(clientItem);\r\n });\r\n\r\n addSectionTitle(targetDiv);\r\n}\r\n\r\n/**\r\n * Set up environment variables section in the modal\r\n */\r\nexport function setupEnvironmentVariables(mcpServer, envInputsDiv, targetData) {\r\n // For SSE mode, don't show env section at all\r\n if (mcpServer.mode === 'sse' || mcpServer.mode === 'streamable-http') {\r\n envInputsDiv.style.display = 'none';\r\n return;\r\n }\r\n\r\n const envRequirements = mcpServer.installation?.['env'] || mcpServer.installation?.env || {};\r\n addEnvironmentTitle(envInputsDiv);\r\n\r\n // Fetch system settings to get userConfigurations for env defaults\r\n fetch('/api/settings')\r\n .then(response => response.json())\r\n .then(data => {\r\n const userConfigurations = (data && data.data && data.data.userConfigurations) ? data.data.userConfigurations : {};\r\n if (Object.keys(envRequirements).length === 0) {\r\n addNoEnvMessage(envInputsDiv);\r\n } else {\r\n createEnvironmentInputs(envRequirements, envInputsDiv, targetData.clientMcpSettings, mcpServer.name, userConfigurations);\r\n }\r\n })\r\n .catch(() => {\r\n // fallback to old logic if fetch fails\r\n if (Object.keys(envRequirements).length === 0) {\r\n addNoEnvMessage(envInputsDiv);\r\n } else {\r\n createEnvironmentInputs(envRequirements, envInputsDiv, targetData.clientMcpSettings, mcpServer.name, {});\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Set up installation arguments section in the modal\r\n */\r\nexport function setupInstallationArguments(installation, modalArguments, categoryName, mcpServer) {\r\n if (!installation) {\r\n modalArguments.style.display = 'none';\r\n return;\r\n }\r\n\r\n if (mcpServer?.mode === 'sse' || mcpServer?.mode === 'streamable-http') {\r\n // For SSE mode, show URL and Starting Command if available\r\n modalArguments.innerHTML = ''; // Clear existing content\r\n let sseInfoAdded = false;\r\n if (installation.url) {\r\n addArgumentsTitle(modalArguments); // Add title only if there's content\r\n createReadOnlyTextBox(modalArguments, 'Server URL', installation.url, 'sse_url');\r\n sseInfoAdded = true;\r\n }\r\n if (installation.startingCommand) {\r\n if (!sseInfoAdded) addArgumentsTitle(modalArguments); // Add title if not already added\r\n createReadOnlyTextBox(modalArguments, 'Starting Command', installation.startingCommand, 'sse_starting_command');\r\n sseInfoAdded = true;\r\n }\r\n if (!sseInfoAdded) {\r\n modalArguments.style.display = 'none'; // Hide if no SSE info to show\r\n }\r\n return;\r\n }\r\n\r\n // For non-SSE modes, keep the existing logic\r\n addArgumentsTitle(modalArguments);\r\n const container = createArgumentsContainer();\r\n modalArguments.appendChild(container);\r\n\r\n if (installation.args && Array.isArray(installation.args)) {\r\n installation.args.forEach(arg => {\r\n container.appendChild(createArgumentInput(arg));\r\n });\r\n } else {\r\n container.appendChild(createArgumentInput());\r\n }\r\n\r\n if (installation.command === 'python' || installation.command.includes('python')) {\r\n addPythonEnvironmentInput(categoryName, mcpServer.name, modalArguments);\r\n }\r\n}\r\n\r\n/**\r\n * Set up server requirements section in the modal\r\n */\r\nexport function setupServerRequirements(mcpServer, serverData, categoryName, serverName, modalRequirements) {\r\n // For SSE mode, don't show requirements section at all\r\n if (mcpServer.mode === 'sse' || mcpServer.mode === 'streamable-http') {\r\n modalRequirements.style.display = 'none';\r\n return;\r\n }\r\n\r\n const serverRequirements = mcpServer.dependencies?.requirements || [];\r\n const requirements = serverData.data.installationStatus?.requirementsStatus || {};\r\n\r\n if (serverRequirements.length > 0) {\r\n const reqHtml = renderRequirements(serverRequirements, requirements, categoryName, serverName);\r\n modalRequirements.innerHTML = `\r\n <h3 class=\"text-lg font-semibold text-gray-700 mb-3\">Dependencies</h3>\r\n <p class=\"text-sm text-gray-600 mb-4\">These dependencies will be automatically installed when installing the server</p>\r\n ${reqHtml}\r\n `;\r\n\r\n setupUpdateToggles(modalRequirements);\r\n } else {\r\n modalRequirements.innerHTML = '<p class=\"text-gray-600\">No additional dependencies required.</p>';\r\n }\r\n}\r\n\r\n/**\r\n * Set up the form submit handler\r\n */\r\nexport function setupFormSubmitHandler(form, envInputsDiv, modalArguments, modalRequirements, categoryName, serverName, serverStatuses, serverData, mcpServer) {\r\n form.onsubmit = (e) => {\r\n e.preventDefault();\r\n\r\n const envVars = {};\r\n const args = [];\r\n const requirementsToUpdate = [];\r\n let pythonEnv = undefined;\r\n\r\n // Only collect env, args and requirements if not in SSE mode\r\n if (mcpServer?.mode !== 'sse' && mcpServer?.mode !== 'streamable-http') {\r\n const inputs = envInputsDiv.querySelectorAll('input');\r\n inputs.forEach(input => {\r\n if (input.name && input.value) {\r\n envVars[input.name] = input.value;\r\n }\r\n });\r\n\r\n const argInputs = modalArguments.querySelectorAll('.arg-input');\r\n args.push(...Array.from(argInputs)\r\n .map(input => input.value.trim())\r\n .filter(val => val !== ''));\r\n\r\n const pythonEnvInput = document.getElementById('python_env');\r\n pythonEnv = pythonEnvInput?.value?.trim();\r\n\r\n const updateToggles = modalRequirements.querySelectorAll('.toggle-update:checked');\r\n updateToggles.forEach(toggle => {\r\n requirementsToUpdate.push({\r\n name: toggle.dataset.name,\r\n version: toggle.dataset.version\r\n });\r\n });\r\n }\r\n\r\n // Only POST userConfigurations and/or pythonEnvs if there is a difference, then proceed with install\r\n fetch('/api/settings')\r\n .then(response => response.json())\r\n .then(data => {\r\n const userConfigurations = (data && data.data && data.data.userConfigurations) ? data.data.userConfigurations : {};\r\n const pythonEnvs = (data && data.data && data.data.pythonEnvs) ? data.data.pythonEnvs : {};\r\n const updatedUserConfigurations = { ...userConfigurations };\r\n const updatedPythonEnvs = { ...pythonEnvs };\r\n\r\n Object.keys(envVars).forEach(key => {\r\n updatedUserConfigurations[key] = envVars[key];\r\n });\r\n\r\n // Check for userConfigurations difference\r\n let needsUserConfigUpdate = false;\r\n const allUserConfigKeys = new Set([...Object.keys(userConfigurations), ...Object.keys(envVars)]);\r\n for (const key of allUserConfigKeys) {\r\n if (userConfigurations[key] !== updatedUserConfigurations[key]) {\r\n needsUserConfigUpdate = true;\r\n break;\r\n }\r\n }\r\n\r\n // Check for pythonEnvs difference\r\n let needsPythonEnvUpdate = false;\r\n if (pythonEnv !== undefined && pythonEnv !== pythonEnvs[`${categoryName}:${serverName}`]) {\r\n updatedPythonEnvs[`${categoryName}:${serverName}`] = pythonEnv;\r\n needsPythonEnvUpdate = true;\r\n }\r\n\r\n // If either needs update, POST the merged object\r\n if (needsUserConfigUpdate || needsPythonEnvUpdate) {\r\n const postBody = {};\r\n if (needsUserConfigUpdate) postBody.userConfigurations = updatedUserConfigurations;\r\n if (needsPythonEnvUpdate) postBody.pythonEnvs = updatedPythonEnvs;\r\n fetch('/api/settings', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(postBody)\r\n }).then(() => {\r\n proceedInstall();\r\n }).catch(() => {\r\n proceedInstall();\r\n });\r\n } else {\r\n proceedInstall();\r\n }\r\n })\r\n .catch(() => {\r\n proceedInstall();\r\n });\r\n\r\n function proceedInstall() {\r\n const hasRequirementsToUpdate = requirementsToUpdate.length > 0;\r\n\r\n const uninstallBtn = document.querySelector('.uninstall-btn');\r\n if (!uninstallBtn || !uninstallBtn.matches(':active')) {\r\n const selectedTargets = window.selectedClients;\r\n if (selectedTargets.length === 0 && !hasRequirementsToUpdate) {\r\n showToast('Please select at least one client to configure.', 'error');\r\n return;\r\n }\r\n\r\n let installingMessage = \"Starting installation...\";\r\n const serverStatus = serverStatuses[serverName] || { installedStatus: {} };\r\n if (selectedTargets.length > 0) {\r\n const target = selectedTargets[0];\r\n const msg = serverStatus.installedStatus?.[target]?.message;\r\n if (msg) installingMessage = msg;\r\n }\r\n\r\n const serverInstallOptions = {\r\n targetClients: selectedTargets,\r\n env: envVars,\r\n args: args,\r\n settings: pythonEnv ? { pythonEnv: pythonEnv } : {}\r\n };\r\n\r\n if (requirementsToUpdate.length > 0) {\r\n serverInstallOptions.requirements = requirementsToUpdate;\r\n }\r\n\r\n handleBulkClientInstall(categoryName, serverName, selectedTargets, envVars, installingMessage, serverData, serverInstallOptions);\r\n }\r\n }\r\n };\r\n}\r\n\r\n// Helper functions\r\nfunction createClientItem(target, statusText, statusClass, isSelectable, isConfigured, isInProgress) {\r\n const clientItem = document.createElement('div');\r\n clientItem.className = 'client-item';\r\n clientItem.dataset.target = target;\r\n clientItem.dataset.selected = 'false';\r\n\r\n // Create client info (name)\r\n const clientInfo = document.createElement('div');\r\n clientInfo.className = 'client-info';\r\n\r\n // Client name label\r\n const clientName = document.createElement('span');\r\n clientName.className = 'text-sm font-medium text-gray-900';\r\n clientName.textContent = target;\r\n\r\n // Add elements to client info\r\n clientInfo.appendChild(clientName);\r\n\r\n // Add client info to client item\r\n clientItem.appendChild(clientInfo);\r\n\r\n if (!isSelectable) {\r\n clientItem.classList.add('non-selectable');\r\n if (isConfigured) {\r\n clientItem.classList.add('installed-item');\r\n clientItem.title = 'Already installed';\r\n } else if (isInProgress) {\r\n clientItem.classList.add('in-progress-item');\r\n clientItem.title = 'Installation in progress';\r\n }\r\n }\r\n\r\n return clientItem;\r\n}\r\n\r\nfunction setupClientItemListeners(clientItem, target, isSelectable) {\r\n if (isSelectable) {\r\n clientItem.addEventListener('click', () => {\r\n const isSelected = clientItem.dataset.selected === 'true';\r\n clientItem.dataset.selected = isSelected ? 'false' : 'true';\r\n\r\n if (isSelected) {\r\n clientItem.classList.remove('selected');\r\n window.selectedClients = window.selectedClients.filter(c => c !== target);\r\n } else {\r\n clientItem.classList.add('selected');\r\n if (!window.selectedClients.includes(target)) {\r\n window.selectedClients.push(target);\r\n }\r\n }\r\n });\r\n }\r\n}\r\n\r\nfunction setupUninstallButton(clientItem, statusText, operationStatus, target, categoryName, serverName, statusClass) {\r\n if (statusText) {\r\n const statusContainer = document.createElement('div');\r\n statusContainer.className = 'status-container';\r\n\r\n const statusBadge = document.createElement('span');\r\n statusBadge.className = `status-badge ${statusClass}`;\r\n statusBadge.textContent = statusText;\r\n statusContainer.appendChild(statusBadge);\r\n\r\n if (operationStatus.status === 'completed' && operationStatus.type === 'install') {\r\n const uninstallBtn = createUninstallButton(target, categoryName, serverName);\r\n statusContainer.appendChild(uninstallBtn);\r\n }\r\n\r\n clientItem.appendChild(statusContainer);\r\n }\r\n}\r\n\r\nfunction createUninstallButton(target, categoryName, serverName) {\r\n const uninstallBtn = document.createElement('button');\r\n uninstallBtn.className = 'uninstall-btn text-red-600 hover:text-red-800 ml-2';\r\n uninstallBtn.innerHTML = '<i class=\"bx bx-trash\"></i>';\r\n uninstallBtn.title = 'Uninstall from this client';\r\n\r\n uninstallBtn.onclick = async (e) => {\r\n e.stopPropagation();\r\n e.preventDefault();\r\n\r\n const confirmed = await showConfirm('Uninstall Confirmation',\r\n `Are you sure you want to uninstall ${serverName} from ${target}?`);\r\n\r\n if (confirmed) {\r\n window.selectedClients = [target];\r\n showInstallLoadingModal('Uninstalling');\r\n const serverList = {\r\n [serverName]: {\r\n removeData: true\r\n }\r\n };\r\n\r\n try {\r\n updateOverallInstallStatus('in-progress', `Uninstalling ${serverName} from ${target}...`);\r\n await uninstallTools(categoryName, serverList, [target]);\r\n // uninstallTools will now handle its own final status update via updateOverallInstallStatus\r\n } catch (error) {\r\n updateOverallInstallStatus('failed', `Error during uninstall: ${error.message}`);\r\n // No need to throw here if uninstallTools handles its errors by updating status\r\n }\r\n }\r\n return false;\r\n };\r\n\r\n return uninstallBtn;\r\n}\r\n\r\nfunction addSectionTitle(targetDiv) {\r\n if (!targetDiv.querySelector('.section-title')) {\r\n const titleElement = document.createElement('h3');\r\n titleElement.className = 'section-title text-lg font-semibold text-gray-700 mb-3';\r\n titleElement.textContent = 'Client Status';\r\n targetDiv.insertBefore(titleElement, targetDiv.firstChild);\r\n }\r\n}\r\n\r\nfunction addEnvironmentTitle(envInputsDiv) {\r\n if (!envInputsDiv.querySelector('.section-title')) {\r\n const envTitle = document.createElement('h3');\r\n envTitle.className = 'section-title text-lg font-semibold text-gray-700 mb-3';\r\n envTitle.textContent = 'Environment Variables';\r\n envInputsDiv.insertBefore(envTitle, envInputsDiv.firstChild);\r\n }\r\n}\r\n\r\nfunction addNoEnvMessage(envInputsDiv) {\n const noEnvMessage = document.createElement('p');\n noEnvMessage.className = 'text-gray-600';\n noEnvMessage.textContent = 'No environment variables required for this MCP server.';\n envInputsDiv.appendChild(noEnvMessage);\n}\n\nfunction getClientEnvValue(clientSettings, serverName, key) {\n if (!clientSettings) {\n return null;\n }\n\n const clientNames = Object.keys(clientSettings);\n for (const name of clientNames) {\n const settings = clientSettings[name];\n if (!settings || !settings.mcpServers || !settings.mcpServers[serverName] ||\n !settings.mcpServers[serverName].env) {\n continue;\n }\n\n const value = settings.mcpServers[serverName].env[key];\n if (value) {\n return value;\n }\n }\n\n return null;\n}\n\nfunction createEnvironmentInputs(envRequirements, envInputsDiv, clientSettings, serverName, userConfigurations = {}) {\n Object.keys(envRequirements).forEach(key => {\n const req = envRequirements[key];\n const inputId = `env_${key}`;\n const inputWrapper = document.createElement('div');\n inputWrapper.className = 'mb-3';\r\n\r\n const label = document.createElement('label');\r\n label.htmlFor = inputId;\r\n label.className = 'block text-sm font-medium text-gray-700 mb-1';\r\n label.innerHTML = `${key} ${req.Required ? '<span class=\"text-red-500\">*</span>' : ''}`;\r\n\r\n const input = document.createElement('input');\r\n input.type = req.isSecret ? 'password' : 'text';\r\n input.id = inputId;\r\n input.name = key;\r\n input.placeholder = req.Description || key;\r\n input.required = req.Required;\r\n input.className = 'input-field';\r\n\r\n // For default value, first check userConfigurations, then client settings, then provided default\r\n let defaultValue = req.Default || '';\r\n\n // Use userConfigurations if present\n if (userConfigurations && userConfigurations[key]) {\n defaultValue = userConfigurations[key];\n } else {\n const storedValue = getClientEnvValue(clientSettings, serverName, key);\n if (storedValue) {\n defaultValue = storedValue;\n }\n }\n\n input.value = defaultValue;\n\r\n inputWrapper.appendChild(label);\r\n inputWrapper.appendChild(input);\r\n\r\n if (req.Description) {\r\n const description = document.createElement('p');\r\n description.className = 'text-xs text-gray-500 mt-1';\r\n description.textContent = req.Description;\r\n inputWrapper.appendChild(description);\r\n }\r\n\r\n envInputsDiv.appendChild(inputWrapper);\r\n });\r\n}\r\n\r\nfunction addArgumentsTitle(modalArguments) {\r\n const argsTitle = document.createElement('h3');\r\n argsTitle.className = 'section-title text-lg font-semibold text-gray-700 mb-3';\r\n argsTitle.textContent = 'Arguments';\r\n modalArguments.appendChild(argsTitle);\r\n}\r\n\r\nfunction createArgumentsContainer() {\r\n const container = document.createElement('div');\r\n container.className = 'args-container mb-3';\r\n\r\n const addButton = document.createElement('button');\r\n addButton.type = 'button';\r\n addButton.className = 'add-arg-button px-3 py-1 text-sm text-blue-600 hover:text-blue-800 border border-blue-600 hover:border-blue-800 rounded-md mb-2';\r\n addButton.innerHTML = '<i class=\"bx bx-plus\"></i> Add Argument';\r\n addButton.onclick = () => container.appendChild(createArgumentInput());\r\n\r\n container.appendChild(addButton);\r\n return container;\r\n}\r\n\r\nfunction createArgumentInput(value = '') {\r\n const argWrapper = document.createElement('div');\r\n argWrapper.className = 'arg-wrapper flex items-center gap-2 mb-2';\r\n\r\n const input = document.createElement('input');\r\n input.type = 'text';\r\n input.className = 'arg-input flex-grow';\r\n input.value = value;\r\n input.placeholder = 'Enter argument value';\r\n\r\n const removeButton = document.createElement('button');\r\n removeButton.type = 'button';\r\n removeButton.className = 'remove-arg-button text-red-600 hover:text-red-800';\r\n removeButton.innerHTML = '<i class=\"bx bx-trash\"></i>';\r\n removeButton.onclick = () => argWrapper.remove();\r\n\r\n argWrapper.appendChild(input);\r\n argWrapper.appendChild(removeButton);\r\n return argWrapper;\r\n}\r\n\r\n/**\r\n * Creates a read-only text box with a label, typically for displaying SSE server info.\r\n * Appends the created element to the parentElement.\r\n */\r\nfunction createReadOnlyTextBox(parentElement, labelText, value, id) {\r\n const wrapper = document.createElement('div');\r\n wrapper.className = 'mb-3 sse-info-field'; // Added for styling consistency\r\n\r\n const label = document.createElement('label');\r\n label.htmlFor = id;\r\n label.className = 'block text-sm font-medium text-gray-700 mb-1';\r\n label.textContent = labelText;\r\n\r\n const textBox = document.createElement('input');\r\n textBox.type = 'text';\r\n textBox.id = id;\r\n textBox.value = value;\r\n textBox.readOnly = true;\r\n textBox.className = 'input-field sse-input-readonly w-full'; // Reuses 'input-field' and adds specific class for styling, w-full for width\r\n\r\n wrapper.appendChild(label);\r\n wrapper.appendChild(textBox);\r\n parentElement.appendChild(wrapper); // Append directly to the provided parent\r\n}\r\nfunction addPythonEnvironmentInput(categoryName, serverName, modalArguments) {\r\n const pythonEnvWrapper = document.createElement('div');\r\n pythonEnvWrapper.className = 'mt-4';\r\n\r\n const pythonEnvLabel = document.createElement('label');\r\n pythonEnvLabel.htmlFor = 'python_env';\r\n pythonEnvLabel.className = 'block text-sm font-medium text-gray-700 mb-1';\r\n pythonEnvLabel.textContent = 'Python Environment';\r\n\r\n const pythonEnvInput = document.createElement('input');\r\n pythonEnvInput.type = 'text';\r\n pythonEnvInput.id = 'python_env';\r\n pythonEnvInput.className = 'input-field';\r\n pythonEnvInput.placeholder = 'Enter Python environment path (optional)';\r\n\r\n const envDescription = document.createElement('p');\r\n envDescription.className = 'text-xs text-gray-500 mt-1';\r\n envDescription.textContent = 'Specify the Python exectable file(e.g. C:/python312/python) to use for installation. Leave empty to use system default. You can specify value in Settings page';\r\n\r\n pythonEnvWrapper.appendChild(pythonEnvLabel);\r\n pythonEnvWrapper.appendChild(pythonEnvInput);\r\n pythonEnvWrapper.appendChild(envDescription);\r\n modalArguments.appendChild(pythonEnvWrapper);\r\n // Fetch system settings and set default value for python_env\r\n fetch('/api/settings')\r\n .then(response => response.json())\r\n .then(data => {\r\n if (data && data.data && data.data.pythonEnvs) {\r\n pythonEnvInput.value = data.data.pythonEnvs[`${categoryName}:${serverName}`] || data.data.pythonEnvs['system'] || '';\r\n }\r\n })\r\n .catch(() => {\r\n // Ignore errors, leave input empty\r\n });\r\n}\r\n\r\nfunction renderRequirements(serverRequirements, requirements, categoryName, serverName) {\r\n return serverRequirements.map(req => {\r\n const status = requirements[req.name] || {};\r\n let statusClass = status.installed\r\n ? 'text-green-600 bg-green-50'\r\n : 'text-yellow-600 bg-yellow-50';\r\n let statusText = status.installed ? 'Installed' : 'Required';\r\n let versionDisplay = status.version ? ` • <span class=\"font-medium\">${status.version}</span>` : '';\r\n let updateToggle = '';\r\n\r\n // Check if there's an available update\r\n if (status.installed && status.availableUpdate && status.availableUpdate.version) {\r\n if (status.version && compareVersions(status.availableUpdate.version, status.version) > 0) {\r\n // Show version update information with yellow color and icon\r\n statusClass = 'text-yellow-600 bg-yellow-50';\r\n statusText = `<span style=\"color: #f59e0b; font-weight: bold; margin-right: 5px;\">↑</span>${status.availableUpdate.version}`;\r\n\r\n // Create a toggle switch for update\r\n updateToggle = `\r\n <label class=\"inline-flex items-center cursor-pointer ml-2\">\r\n <input type=\"checkbox\" class=\"toggle-update sr-only\"\r\n data-name=\"${req.name}\"\r\n data-version=\"${status.availableUpdate.version}\"\r\n data-category=\"${categoryName}\"\r\n data-server=\"${serverName}\">\r\n <div class=\"relative w-10 h-5 bg-gray-200 rounded-full toggle-bg\">\r\n <div class=\"absolute inset-y-0 left-0 w-5 h-5 bg-white rounded-full transition-transform duration-300 transform\"></div>\r\n </div>\r\n <span class=\"ml-2 text-sm text-gray-700\">Update</span>\r\n </label>\r\n `;\r\n }\r\n }\r\n\r\n return `\r\n <div class=\"border border-gray-200 p-3 rounded-lg mb-2 hover:bg-gray-50\">\r\n <div class=\"flex justify-between items-center\">\r\n <div>\r\n <div class=\"font-semibold text-gray-800\">${req.name}</div>\r\n <div class=\"text-sm text-gray-600 shadow-sm p-1 rounded bg-gray-50\">\r\n <span class=\"font-medium\">${status.type || 'package'}</span>${versionDisplay}\r\n </div>\r\n </div>\r\n <div class=\"flex items-center\">\r\n <span class=\"${statusClass} inline-flex items-center px-3 py-1 rounded-full text-sm\">\r\n ${statusText}\r\n </span>\r\n ${updateToggle}\r\n </div>\r\n </div>\r\n </div>\r\n `;\r\n }).join('');\r\n}\r\n\r\nfunction setupUpdateToggles(modalRequirements) {\r\n const updateToggles = modalRequirements.querySelectorAll('.toggle-update');\r\n updateToggles.forEach(toggle => {\r\n toggle.addEventListener('change', function () {\r\n const toggleBg = this.parentElement.querySelector('.toggle-bg');\r\n if (this.checked) {\r\n toggleBg.classList.add('bg-blue-500');\r\n toggleBg.querySelector('div').classList.add('translate-x-5');\r\n } else {\r\n toggleBg.classList.remove('bg-blue-500');\r\n toggleBg.querySelector('div').classList.remove('translate-x-5');\r\n }\r\n });\r\n });\r\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":[],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":["closeModal","document","getElementById","style","display","lastSelected","localStorage","getItem","setTimeout","window","location","href","pathname","encodeURIComponent","reload","setupToggleStyles","styleElement","createElement","textContent","head","appendChild"],"sources":["0"],"sourcesContent":["/**\r\n * Close modal and ensure selected category persists\r\n */\r\nexport function closeModal() {\r\n document.getElementById('installModal').style.display = \"none\";\r\n\r\n // Get the last selected category from localStorage\r\n const lastSelected = localStorage.getItem('lastSelectedCategory');\r\n\r\n // Refresh page and restore selection\r\n if (lastSelected) {\r\n setTimeout(() => {\r\n window.location.href = window.location.pathname + '?category=' + encodeURIComponent(lastSelected);\r\n }, 100);\r\n } else {\r\n location.reload();\r\n }\r\n}\r\n\r\n/**\r\n * Setup toggle switch styles\r\n */\r\nexport function setupToggleStyles() {\r\n const styleElement = document.createElement('style');\r\n styleElement.textContent = `\r\n .toggle-bg.bg-blue-500 {\r\n background-color: #3b82f6;\r\n }\r\n .toggle-bg {\r\n transition: background-color 0.3s;\r\n }\r\n .toggle-bg div {\r\n transition: transform 0.3s;\r\n }\r\n `;\r\n document.head.appendChild(styleElement);\r\n}"],"mappings":"OAGO,SAASA,aACZC,SAASC,eAAe,gBAAgBC,MAAMC,QAAU,OAGxD,MAAMC,EAAeC,aAAaC,QAAQ,wBAGtCF,EACAG,YAAW,KACPC,OAAOC,SAASC,KAAOF,OAAOC,SAASE,SAAW,aAAeC,mBAAmBR,EAAa,GAClG,KAEHK,SAASI,QAEjB,QAKO,SAASC,oBACZ,MAAMC,EAAef,SAASgB,cAAc,SAC5CD,EAAaE,YAAc,6PAW3BjB,SAASkB,KAAKC,YAAYJ,EAC9B","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":[],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":["compareVersions","v1","v2","v1Parts","split","map","Number","v2Parts","i","Math","max","length","v1Part","v2Part"],"sources":["0"],"sourcesContent":["/**\r\n * Simple version comparison function\r\n * @param {string} v1 First version\r\n * @param {string} v2 Second version\r\n * @returns {number} 1 if v1 > v2, -1 if v1 < v2, 0 if equal\r\n */\r\nexport function compareVersions(v1, v2) {\r\n const v1Parts = v1.split('.').map(Number);\r\n const v2Parts = v2.split('.').map(Number);\r\n\r\n for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {\r\n const v1Part = v1Parts[i] || 0;\r\n const v2Part = v2Parts[i] || 0;\r\n\r\n if (v1Part > v2Part) return 1;\r\n if (v1Part < v2Part) return -1;\r\n }\r\n\r\n return 0;\r\n}"],"mappings":"OAMO,SAASA,gBAAgBC,EAAIC,GAChC,MAAMC,EAAUF,EAAGG,MAAM,KAAKC,IAAIC,QAC5BC,EAAUL,EAAGE,MAAM,KAAKC,IAAIC,QAElC,IAAK,IAAIE,EAAI,EAAGA,EAAIC,KAAKC,IAAIP,EAAQQ,OAAQJ,EAAQI,QAASH,IAAK,CAC/D,MAAMI,EAAST,EAAQK,IAAM,EACvBK,EAASN,EAAQC,IAAM,EAE7B,GAAII,EAASC,EAAQ,OAAO,EAC5B,GAAID,EAASC,EAAQ,OAAQ,CACjC,CAEA,OAAO,CACX","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":[],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":["showInstallModal","closeModal","uninstallTools","showInstallLoadingModal","hideInstallLoadingModal","window","document","addEventListener","console","log","lastSelectedCategory","localStorage","getItem","lastSelectedServerName","isCategoryValid","trim","isServerNameValid","warn"],"sources":["0"],"sourcesContent":["// Import all functionality from the modal folder\r\nimport {\r\n showInstallModal,\r\n closeModal,\r\n uninstallTools,\r\n showInstallLoadingModal,\r\n // appendInstallLoadingMessage, // Removed\r\n hideInstallLoadingModal,\r\n // Potentially add updateOverallInstallStatus, addInstallationStep if needed from ./modal/index.js\r\n} from './modal/index.js';\r\n\r\n// Re-export all modal functionality\r\nexport {\r\n showInstallModal,\r\n closeModal,\r\n uninstallTools,\r\n showInstallLoadingModal, // Added to exports\r\n hideInstallLoadingModal // Added to exports\r\n // Potentially add updateOverallInstallStatus, addInstallationStep to exports\r\n};\r\n\r\n// Make certain functions available globally\r\nwindow.showInstallModal = showInstallModal;\r\nwindow.showInstallLoadingModal = showInstallLoadingModal;\r\n// window.appendInstallLoadingMessage = appendInstallLoadingMessage; // Removed\r\nwindow.hideInstallLoadingModal = hideInstallLoadingModal;\r\n\r\n// Listen for the custom event to refresh the main modal content\r\ndocument.addEventListener('refreshMainModalContent', () => {\r\n console.log('[ModalJS] Received refreshMainModalContent event. Refreshing main modal.');\r\n const lastSelectedCategory = localStorage.getItem('lastSelectedCategory');\r\n const lastSelectedServerName = localStorage.getItem('lastSelectedServerName');\r\n\r\n const isCategoryValid = lastSelectedCategory && lastSelectedCategory.trim() !== '' && lastSelectedCategory !== 'undefined' && lastSelectedCategory !== 'null';\r\n const isServerNameValid = lastSelectedServerName && lastSelectedServerName.trim() !== '' && lastSelectedServerName !== 'undefined' && lastSelectedServerName !== 'null';\r\n\r\n if (isCategoryValid && isServerNameValid) {\r\n window.showInstallModal(lastSelectedCategory, lastSelectedServerName);\r\n } else {\r\n console.warn(`[ModalJS] Not refreshing modal. Category valid: ${isCategoryValid} (value: \"${lastSelectedCategory}\"), ServerName valid: ${isServerNameValid} (value: \"${lastSelectedServerName}\")`);\r\n }\r\n});\r\n"],"mappings":"OAEIA,iBACAC,WACAC,eACAC,wBAEAC,4BAEG,0BAIHJ,iBACAC,WACAC,eACAC,wBACAC,yBAKJC,OAAOL,iBAAmBA,iBAC1BK,OAAOF,wBAA0BA,wBAEjCE,OAAOD,wBAA0BA,wBAGjCE,SAASC,iBAAiB,2BAA2B,KACjDC,QAAQC,IAAI,4EACZ,MAAMC,EAAuBC,aAAaC,QAAQ,wBAC5CC,EAAyBF,aAAaC,QAAQ,0BAE9CE,EAAkBJ,GAAwD,KAAhCA,EAAqBK,QAA0C,cAAzBL,GAAiE,SAAzBA,EACxHM,EAAoBH,GAA4D,KAAlCA,EAAuBE,QAA4C,cAA3BF,GAAqE,SAA3BA,EAElIC,GAAmBE,EACnBX,OAAOL,iBAAiBU,EAAsBG,GAE9CL,QAAQS,KAAK,mDAAmDH,cAA4BJ,0BAA6CM,cAA8BH,MAC3K","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":[],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":["showToast","message","type","alertContainer","document","querySelector","console","error","alertId","Date","now","iconHtml","alertHtml","insertAdjacentHTML","alertElement","getElementById","requestAnimationFrame","classList","add","closeButton","dismissAlert","remove","addEventListener","once","setTimeout","showConfirm","title","Promise","resolve","modalId","modalHtml","body","modalElement","style","display","event","target","data","sessionStorage","getItem","JSON","parse","removeItem"],"sources":["0"],"sourcesContent":["// Function to show a toast notification\nfunction showToast(message, type = 'success') {\n const alertContainer = document.querySelector('.alert-container');\n if (!alertContainer) {\n console.error('Alert container not found. Notifications will not be displayed.');\n return;\n }\n const alertId = `alert-${Date.now()}`;\n\n let iconHtml = '';\n switch (type) {\n case 'success':\n iconHtml = '<i class=\"bi bi-check-circle-fill alert-icon\"></i>';\n break;\n case 'error':\n iconHtml = '<i class=\"bi bi-x-octagon-fill alert-icon\"></i>'; // Changed icon for error\n break;\n case 'info':\n iconHtml = '<i class=\"bi bi-info-circle-fill alert-icon\"></i>';\n break;\n case 'warning':\n iconHtml = '<i class=\"bi bi-exclamation-triangle-fill alert-icon\"></i>';\n break;\n default:\n iconHtml = '<i class=\"bi bi-bell-fill alert-icon\"></i>'; // Default icon\n }\n\n const alertHtml = `\n <div id=\"${alertId}\" class=\"alert alert-${type}\" role=\"alert\">\n ${iconHtml}\n <span>${message}</span>\n <button type=\"button\" class=\"btn-close\" aria-label=\"Close\"></button>\n </div>\n `;\n\n alertContainer.insertAdjacentHTML('beforeend', alertHtml);\n\n const alertElement = document.getElementById(alertId);\n if (!alertElement) return;\n\n // Trigger the slide-in animation\n // Adding a slight delay to ensure the element is in the DOM before adding the 'show' class\n requestAnimationFrame(() => {\n requestAnimationFrame(() => { // Double requestAnimationFrame for more reliable transition start\n alertElement.classList.add('show');\n });\n });\n\n\n const closeButton = alertElement.querySelector('.btn-close');\n\n const dismissAlert = () => {\n alertElement.classList.remove('show'); // Trigger slide-out animation\n // Wait for fade out animation to complete before removing\n alertElement.addEventListener('transitionend', () => {\n alertElement.remove();\n }, { once: true }); // Ensure event listener is removed after execution\n };\n\n if (closeButton) {\n closeButton.addEventListener('click', dismissAlert);\n }\n\n // Auto-dismiss after 5 seconds (increased from 3)\n setTimeout(dismissAlert, 5000);\n}\n\n// Function to show a confirmation dialog using custom modal system\nfunction showConfirm(title, message) {\n return new Promise((resolve) => {\n const modalId = `confirm-modal-${Date.now()}`;\n const modalHtml = `\n <div id=\"${modalId}\" class=\"modal\" style=\"display: none;\">\n <div class=\"modal-content\" style=\"max-width: 400px; margin: 15% auto;\">\n <div class=\"modal-header\">\n <h3 class=\"text-lg font-semibold text-gray-700\">${title}</h3>\n </div>\n <div style=\"margin: 1rem 0;\">\n ${message}\n </div>\n <div style=\"display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1rem;\">\n <button type=\"button\" class=\"cancel-button px-4 py-2 text-gray-600 hover:text-gray-800 font-medium rounded-lg hover:bg-gray-100 transition-colors\">\n Cancel\n </button>\n <button type=\"button\" class=\"confirm-button submit-button\">\n Confirm\n </button>\n </div>\n </div>\n </div>\n `;\n\n document.body.insertAdjacentHTML('beforeend', modalHtml);\n\n const modalElement = document.getElementById(modalId);\n // Trigger display after a short delay to allow CSS transitions if any are added later for modals\n requestAnimationFrame(() => {\n modalElement.style.display = 'block';\n });\n\n\n // Handle confirm button click\n modalElement.querySelector('.confirm-button').addEventListener('click', () => {\n modalElement.style.display = 'none';\n modalElement.remove();\n resolve(true);\n });\n\n // Handle cancel button click\n modalElement.querySelector('.cancel-button').addEventListener('click', () => {\n modalElement.style.display = 'none';\n modalElement.remove();\n resolve(false);\n });\n\n // Handle click outside modal\n modalElement.addEventListener('click', (event) => {\n if (event.target === modalElement) {\n modalElement.style.display = 'none';\n modalElement.remove();\n resolve(false);\n }\n });\n });\n}\n\nexport { showToast, showConfirm };\n\n// Display install notification after page reload\ndocument.addEventListener('DOMContentLoaded', () => {\n const data = sessionStorage.getItem('installNotification');\n if (data) {\n const { message, type } = JSON.parse(data);\n showToast(message, type);\n sessionStorage.removeItem('installNotification');\n }\n});\n"],"mappings":"AACA,SAASA,UAAUC,EAASC,EAAO,WAC/B,MAAMC,EAAiBC,SAASC,cAAc,oBAC9C,IAAKF,EAED,YADAG,QAAQC,MAAM,mEAGlB,MAAMC,EAAU,SAASC,KAAKC,QAE9B,IAAIC,EAAW,GACf,OAAQT,GACJ,IAAK,UACDS,EAAW,qDACX,MACJ,IAAK,QACDA,EAAW,kDACX,MACJ,IAAK,OACDA,EAAW,oDACX,MACJ,IAAK,UACDA,EAAW,6DACX,MACJ,QACIA,EAAW,6CAGnB,MAAMC,EAAY,sBACHJ,yBAA+BN,iCACpCS,wBACMV,mHAKhBE,EAAeU,mBAAmB,YAAaD,GAE/C,MAAME,EAAeV,SAASW,eAAeP,GAC7C,IAAKM,EAAc,OAInBE,uBAAsB,KAClBA,uBAAsB,KAClBF,EAAaG,UAAUC,IAAI,OAAO,GACpC,IAIN,MAAMC,EAAcL,EAAaT,cAAc,cAEzCe,aAAe,KACjBN,EAAaG,UAAUI,OAAO,QAE9BP,EAAaQ,iBAAiB,iBAAiB,KAC3CR,EAAaO,QAAQ,GACtB,CAAEE,MAAM,GAAO,EAGlBJ,GACAA,EAAYG,iBAAiB,QAASF,cAI1CI,WAAWJ,aAAc,IAC7B,CAGA,SAASK,YAAYC,EAAOzB,GACxB,OAAO,IAAI0B,SAASC,IAChB,MAAMC,EAAU,iBAAiBpB,KAAKC,QAChCoB,EAAY,0BACHD,8PAGmDH,kHAGhDzB,+mBAclBG,SAAS2B,KAAKlB,mBAAmB,YAAaiB,GAE9C,MAAME,EAAe5B,SAASW,eAAec,GAE7Cb,uBAAsB,KAClBgB,EAAaC,MAAMC,QAAU,OAAO,IAKxCF,EAAa3B,cAAc,mBAAmBiB,iBAAiB,SAAS,KACpEU,EAAaC,MAAMC,QAAU,OAC7BF,EAAaX,SACbO,GAAQ,EAAK,IAIjBI,EAAa3B,cAAc,kBAAkBiB,iBAAiB,SAAS,KACnEU,EAAaC,MAAMC,QAAU,OAC7BF,EAAaX,SACbO,GAAQ,EAAM,IAIlBI,EAAaV,iBAAiB,SAAUa,IAChCA,EAAMC,SAAWJ,IACjBA,EAAaC,MAAMC,QAAU,OAC7BF,EAAaX,SACbO,GAAQ,GACZ,GACF,GAEV,QAES5B,UAAWyB,aAGpBrB,SAASkB,iBAAiB,oBAAoB,KAC1C,MAAMe,EAAOC,eAAeC,QAAQ,uBACpC,GAAIF,EAAM,CACN,MAAMpC,QAAEA,EAAOC,KAAEA,GAASsC,KAAKC,MAAMJ,GACrCrC,UAAUC,EAASC,GACnBoC,eAAeI,WAAW,sBAC9B","ignoreList":[]}