imcp 0.1.7 → 0.1.8-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/install.js +1 -106
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/list.js +1 -90
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/pull.js +1 -16
- package/dist/cli/commands/pull.js.map +1 -0
- package/dist/cli/commands/serve.js +1 -33
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/uninstall.js +1 -46
- package/dist/cli/commands/uninstall.js.map +1 -0
- package/dist/cli/index.js +1 -65
- package/dist/cli/index.js.map +1 -0
- package/dist/core/installers/clients/BaseClientInstaller.js +1 -282
- package/dist/core/installers/clients/BaseClientInstaller.js.map +1 -0
- package/dist/core/installers/clients/ClientInstaller.js +1 -163
- package/dist/core/installers/clients/ClientInstaller.js.map +1 -0
- package/dist/core/installers/clients/ClientInstallerFactory.js +1 -36
- package/dist/core/installers/clients/ClientInstallerFactory.js.map +1 -0
- package/dist/core/installers/clients/ClineInstaller.js +1 -30
- package/dist/core/installers/clients/ClineInstaller.js.map +1 -0
- package/dist/core/installers/clients/ExtensionInstaller.js +1 -151
- package/dist/core/installers/clients/ExtensionInstaller.js.map +1 -0
- package/dist/core/installers/clients/GithubCopilotInstaller.js +1 -68
- package/dist/core/installers/clients/GithubCopilotInstaller.js.map +1 -0
- package/dist/core/installers/clients/MSRooCodeInstaller.js +1 -28
- package/dist/core/installers/clients/MSRooCodeInstaller.js.map +1 -0
- package/dist/core/installers/index.js +1 -8
- package/dist/core/installers/index.js.map +1 -0
- package/dist/core/installers/requirements/BaseInstaller.js +1 -56
- package/dist/core/installers/requirements/BaseInstaller.js.map +1 -0
- package/dist/core/installers/requirements/CommandInstaller.js +1 -213
- package/dist/core/installers/requirements/CommandInstaller.js.map +1 -0
- package/dist/core/installers/requirements/GeneralInstaller.js +1 -126
- package/dist/core/installers/requirements/GeneralInstaller.js.map +1 -0
- package/dist/core/installers/requirements/InstallerFactory.js +1 -99
- package/dist/core/installers/requirements/InstallerFactory.js.map +1 -0
- package/dist/core/installers/requirements/NpmInstaller.js +1 -235
- package/dist/core/installers/requirements/NpmInstaller.js.map +1 -0
- package/dist/core/installers/requirements/NugetInstaller.js +1 -188
- package/dist/core/installers/requirements/NugetInstaller.js.map +1 -0
- package/dist/core/installers/requirements/PipInstaller.js +1 -192
- package/dist/core/installers/requirements/PipInstaller.js.map +1 -0
- package/dist/core/installers/requirements/RequirementInstaller.js +1 -2
- package/dist/core/installers/requirements/RequirementInstaller.js.map +1 -0
- package/dist/core/loaders/ConfigurationLoader.js +1 -256
- package/dist/core/loaders/ConfigurationLoader.js.map +1 -0
- package/dist/core/loaders/ConfigurationProvider.js +1 -383
- package/dist/core/loaders/ConfigurationProvider.js.map +1 -0
- package/dist/core/loaders/InstallOperationManager.js +1 -310
- package/dist/core/loaders/InstallOperationManager.js.map +1 -0
- package/dist/core/loaders/ServerSchemaLoader.js +1 -108
- package/dist/core/loaders/ServerSchemaLoader.js.map +1 -0
- package/dist/core/loaders/ServerSchemaProvider.js +1 -89
- package/dist/core/loaders/ServerSchemaProvider.js.map +1 -0
- package/dist/core/loaders/SystemSettingsManager.js +1 -256
- package/dist/core/loaders/SystemSettingsManager.js.map +1 -0
- package/dist/core/metadatas/constants.js +1 -100
- package/dist/core/metadatas/constants.js.map +1 -0
- package/dist/core/metadatas/recordingConstants.js +1 -46
- package/dist/core/metadatas/recordingConstants.js.map +1 -0
- package/dist/core/metadatas/types.js +1 -15
- package/dist/core/metadatas/types.js.map +1 -0
- package/dist/core/onboard/FeedOnboardService.js +1 -422
- package/dist/core/onboard/FeedOnboardService.js.map +1 -0
- package/dist/core/onboard/OnboardProcessor.js +1 -333
- package/dist/core/onboard/OnboardProcessor.js.map +1 -0
- package/dist/core/onboard/OnboardStatus.js +1 -9
- package/dist/core/onboard/OnboardStatus.js.map +1 -0
- package/dist/core/onboard/OnboardStatusManager.js +1 -360
- package/dist/core/onboard/OnboardStatusManager.js.map +1 -0
- package/dist/core/validators/FeedValidator.js +1 -133
- package/dist/core/validators/FeedValidator.js.map +1 -0
- package/dist/core/validators/IServerValidator.js +1 -1
- package/dist/core/validators/IServerValidator.js.map +1 -0
- package/dist/core/validators/SSEServerValidator.js +1 -38
- package/dist/core/validators/SSEServerValidator.js.map +1 -0
- package/dist/core/validators/ServerValidatorFactory.js +1 -44
- package/dist/core/validators/ServerValidatorFactory.js.map +1 -0
- package/dist/core/validators/StdioServerValidator.js +1 -281
- package/dist/core/validators/StdioServerValidator.js.map +1 -0
- package/dist/index.js +1 -18
- package/dist/index.js.map +1 -0
- package/dist/services/InstallationService.js +1 -81
- package/dist/services/InstallationService.js.map +1 -0
- package/dist/services/MCPManager.js +1 -197
- package/dist/services/MCPManager.js.map +1 -0
- package/dist/services/RequirementService.js +1 -548
- package/dist/services/RequirementService.js.map +1 -0
- package/dist/services/ServerService.js +1 -127
- package/dist/services/ServerService.js.map +1 -0
- package/dist/services/TelemetryService.js +1 -53
- package/dist/services/TelemetryService.js.map +1 -0
- package/dist/utils/UpdateCheckTracker.js +1 -79
- package/dist/utils/UpdateCheckTracker.js.map +1 -0
- package/dist/utils/adoUtils.js +1 -254
- package/dist/utils/adoUtils.js.map +1 -0
- package/dist/utils/clientUtils.js +1 -65
- package/dist/utils/clientUtils.js.map +1 -0
- package/dist/utils/feedUtils.js +1 -28
- package/dist/utils/feedUtils.js.map +1 -0
- package/dist/utils/githubAuth.js +1 -177
- package/dist/utils/githubAuth.js.map +1 -0
- package/dist/utils/githubUtils.js +1 -125
- package/dist/utils/githubUtils.js.map +1 -0
- package/dist/utils/logger.js +1 -176
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/macroExpressionUtils.js +1 -93
- package/dist/utils/macroExpressionUtils.js.map +1 -0
- package/dist/utils/osUtils.js +1 -664
- package/dist/utils/osUtils.js.map +1 -0
- package/dist/utils/versionUtils.js +1 -101
- package/dist/utils/versionUtils.js.map +1 -0
- package/dist/web/contract/serverContract.js +1 -1
- package/dist/web/contract/serverContract.js.map +1 -0
- package/dist/web/public/js/api.js +2 -132
- package/dist/web/public/js/api.js.map +1 -0
- package/dist/web/public/js/detailsWidget.js +2 -264
- package/dist/web/public/js/detailsWidget.js.map +1 -0
- package/dist/web/public/js/flights/flights.js +2 -127
- package/dist/web/public/js/flights/flights.js.map +1 -0
- package/dist/web/public/js/modal/index.js +2 -52
- package/dist/web/public/js/modal/index.js.map +1 -0
- package/dist/web/public/js/modal/installModal.js +2 -162
- package/dist/web/public/js/modal/installModal.js.map +1 -0
- package/dist/web/public/js/modal/installation.js +2 -266
- package/dist/web/public/js/modal/installation.js.map +1 -0
- package/dist/web/public/js/modal/loadingModal.js +2 -182
- package/dist/web/public/js/modal/loadingModal.js.map +1 -0
- package/dist/web/public/js/modal/modalSetup.js +2 -595
- package/dist/web/public/js/modal/modalSetup.js.map +1 -0
- package/dist/web/public/js/modal/modalUtils.js +2 -37
- package/dist/web/public/js/modal/modalUtils.js.map +1 -0
- package/dist/web/public/js/modal/versionUtils.js +2 -20
- package/dist/web/public/js/modal/versionUtils.js.map +1 -0
- package/dist/web/public/js/modal.js +2 -42
- package/dist/web/public/js/modal.js.map +1 -0
- package/dist/web/public/js/notifications.js +2 -137
- package/dist/web/public/js/notifications.js.map +1 -0
- package/dist/web/public/js/onboard/formProcessor.js +2 -1037
- package/dist/web/public/js/onboard/formProcessor.js.map +1 -0
- package/dist/web/public/js/onboard/index.js +2 -374
- package/dist/web/public/js/onboard/index.js.map +1 -0
- package/dist/web/public/js/onboard/publishHandler.js +2 -172
- package/dist/web/public/js/onboard/publishHandler.js.map +1 -0
- package/dist/web/public/js/onboard/state.js +2 -76
- package/dist/web/public/js/onboard/state.js.map +1 -0
- package/dist/web/public/js/onboard/templates.js +2 -342
- package/dist/web/public/js/onboard/templates.js.map +1 -0
- package/dist/web/public/js/onboard/uiHandlers.js +2 -1076
- package/dist/web/public/js/onboard/uiHandlers.js.map +1 -0
- package/dist/web/public/js/onboard/validationHandlers.js +2 -493
- package/dist/web/public/js/onboard/validationHandlers.js.map +1 -0
- package/dist/web/public/js/serverCategoryDetails.js +2 -364
- package/dist/web/public/js/serverCategoryDetails.js.map +1 -0
- package/dist/web/public/js/serverCategoryList.js +2 -241
- package/dist/web/public/js/serverCategoryList.js.map +1 -0
- package/dist/web/public/js/settings.js +2 -314
- package/dist/web/public/js/settings.js.map +1 -0
- package/dist/web/server.js +1 -404
- package/dist/web/server.js.map +1 -0
- package/package.json +8 -2
- package/.github/ISSUE_TEMPLATE/JitAccess.yml +0 -28
- package/.github/acl/access.yml +0 -20
- package/.github/compliance/inventory.yml +0 -5
- package/.github/policies/jit.yml +0 -19
- package/.github/workflows/build.yml +0 -28
- package/.roo/rules-code/rules.md +0 -88
- package/dist/cli/commands/start.d.ts +0 -2
- package/dist/cli/commands/start.js +0 -32
- package/dist/cli/commands/sync.d.ts +0 -2
- package/dist/cli/commands/sync.js +0 -17
- package/dist/core/ConfigurationLoader.d.ts +0 -32
- package/dist/core/ConfigurationLoader.js +0 -236
- package/dist/core/ConfigurationProvider.d.ts +0 -35
- package/dist/core/ConfigurationProvider.js +0 -375
- package/dist/core/InstallationService.d.ts +0 -50
- package/dist/core/InstallationService.js +0 -350
- package/dist/core/MCPManager.d.ts +0 -28
- package/dist/core/MCPManager.js +0 -188
- package/dist/core/RequirementService.d.ts +0 -40
- package/dist/core/RequirementService.js +0 -110
- package/dist/core/ServerSchemaLoader.d.ts +0 -11
- package/dist/core/ServerSchemaLoader.js +0 -43
- package/dist/core/ServerSchemaProvider.d.ts +0 -17
- package/dist/core/ServerSchemaProvider.js +0 -120
- package/dist/core/constants.d.ts +0 -47
- package/dist/core/constants.js +0 -94
- package/dist/core/installers/BaseInstaller.d.ts +0 -74
- package/dist/core/installers/BaseInstaller.js +0 -253
- package/dist/core/installers/ClientInstaller.d.ts +0 -23
- package/dist/core/installers/ClientInstaller.js +0 -564
- package/dist/core/installers/CommandInstaller.d.ts +0 -37
- package/dist/core/installers/CommandInstaller.js +0 -173
- package/dist/core/installers/GeneralInstaller.d.ts +0 -33
- package/dist/core/installers/GeneralInstaller.js +0 -85
- package/dist/core/installers/InstallerFactory.d.ts +0 -54
- package/dist/core/installers/InstallerFactory.js +0 -97
- package/dist/core/installers/NpmInstaller.d.ts +0 -26
- package/dist/core/installers/NpmInstaller.js +0 -127
- package/dist/core/installers/PipInstaller.d.ts +0 -28
- package/dist/core/installers/PipInstaller.js +0 -127
- package/dist/core/installers/RequirementInstaller.d.ts +0 -33
- package/dist/core/installers/RequirementInstaller.js +0 -3
- package/dist/core/types.d.ts +0 -166
- package/dist/core/types.js +0 -16
- package/dist/services/InstallRequestValidator.d.ts +0 -21
- package/dist/services/InstallRequestValidator.js +0 -99
- package/dist/web/public/js/modal/installHandler.js +0 -227
- package/dist/web/public/js/modal/loadingUI.js +0 -74
- package/dist/web/public/js/modal/messageQueue.js +0 -112
- package/dist/web/public/js/modal/modalUI.js +0 -214
- package/dist/web/public/js/modal/version.js +0 -20
- package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +0 -370
- package/docs/ONBOARDING_PAGE_DESIGN.md +0 -260
- package/docs/Telemetry.md +0 -136
- package/memory-bank/activeContext.md +0 -26
- package/memory-bank/decisionLog.md +0 -91
- package/memory-bank/productContext.md +0 -41
- package/memory-bank/progress.md +0 -35
- package/memory-bank/systemPatterns.md +0 -10
- package/src/cli/commands/install.ts +0 -139
- package/src/cli/commands/list.ts +0 -113
- package/src/cli/commands/pull.ts +0 -16
- package/src/cli/commands/serve.ts +0 -39
- package/src/cli/commands/uninstall.ts +0 -64
- package/src/cli/index.ts +0 -82
- package/src/core/installers/clients/BaseClientInstaller.ts +0 -341
- package/src/core/installers/clients/ClientInstaller.ts +0 -222
- package/src/core/installers/clients/ClientInstallerFactory.ts +0 -43
- package/src/core/installers/clients/ClineInstaller.ts +0 -35
- package/src/core/installers/clients/ExtensionInstaller.ts +0 -165
- package/src/core/installers/clients/GithubCopilotInstaller.ts +0 -79
- package/src/core/installers/clients/MSRooCodeInstaller.ts +0 -32
- package/src/core/installers/index.ts +0 -11
- package/src/core/installers/requirements/BaseInstaller.ts +0 -85
- package/src/core/installers/requirements/CommandInstaller.ts +0 -231
- package/src/core/installers/requirements/GeneralInstaller.ts +0 -133
- package/src/core/installers/requirements/InstallerFactory.ts +0 -114
- package/src/core/installers/requirements/NpmInstaller.ts +0 -271
- package/src/core/installers/requirements/NugetInstaller.ts +0 -203
- package/src/core/installers/requirements/PipInstaller.ts +0 -207
- package/src/core/installers/requirements/RequirementInstaller.ts +0 -42
- package/src/core/loaders/ConfigurationLoader.ts +0 -298
- package/src/core/loaders/ConfigurationProvider.ts +0 -462
- package/src/core/loaders/InstallOperationManager.ts +0 -367
- package/src/core/loaders/ServerSchemaLoader.ts +0 -117
- package/src/core/loaders/ServerSchemaProvider.ts +0 -99
- package/src/core/loaders/SystemSettingsManager.ts +0 -278
- package/src/core/metadatas/constants.ts +0 -122
- package/src/core/metadatas/recordingConstants.ts +0 -65
- package/src/core/metadatas/types.ts +0 -202
- package/src/core/onboard/FeedOnboardService.ts +0 -501
- package/src/core/onboard/OnboardProcessor.ts +0 -356
- package/src/core/onboard/OnboardStatus.ts +0 -60
- package/src/core/onboard/OnboardStatusManager.ts +0 -416
- package/src/core/validators/FeedValidator.ts +0 -135
- package/src/core/validators/IServerValidator.ts +0 -21
- package/src/core/validators/SSEServerValidator.ts +0 -43
- package/src/core/validators/ServerValidatorFactory.ts +0 -51
- package/src/core/validators/StdioServerValidator.ts +0 -313
- package/src/index.ts +0 -44
- package/src/services/InstallationService.ts +0 -102
- package/src/services/MCPManager.ts +0 -249
- package/src/services/RequirementService.ts +0 -627
- package/src/services/ServerService.ts +0 -161
- package/src/services/TelemetryService.ts +0 -59
- package/src/utils/UpdateCheckTracker.ts +0 -86
- package/src/utils/adoUtils.ts +0 -293
- package/src/utils/clientUtils.ts +0 -72
- package/src/utils/feedUtils.ts +0 -31
- package/src/utils/githubAuth.ts +0 -212
- package/src/utils/githubUtils.ts +0 -164
- package/src/utils/logger.ts +0 -195
- package/src/utils/macroExpressionUtils.ts +0 -104
- package/src/utils/osUtils.ts +0 -700
- package/src/utils/versionUtils.ts +0 -114
- package/src/web/contract/serverContract.ts +0 -74
- package/src/web/public/css/detailsWidget.css +0 -235
- package/src/web/public/css/modal.css +0 -757
- package/src/web/public/css/notifications.css +0 -101
- package/src/web/public/css/onboard.css +0 -107
- package/src/web/public/css/serverCategoryList.css +0 -120
- package/src/web/public/css/serverDetails.css +0 -139
- package/src/web/public/index.html +0 -359
- package/src/web/public/js/api.js +0 -132
- package/src/web/public/js/detailsWidget.js +0 -264
- package/src/web/public/js/flights/flights.js +0 -127
- package/src/web/public/js/modal/index.js +0 -52
- package/src/web/public/js/modal/installModal.js +0 -162
- package/src/web/public/js/modal/installation.js +0 -266
- package/src/web/public/js/modal/loadingModal.js +0 -182
- package/src/web/public/js/modal/modalSetup.js +0 -595
- package/src/web/public/js/modal/modalUtils.js +0 -37
- package/src/web/public/js/modal/versionUtils.js +0 -20
- package/src/web/public/js/modal.js +0 -42
- package/src/web/public/js/notifications.js +0 -137
- package/src/web/public/js/onboard/formProcessor.js +0 -1037
- package/src/web/public/js/onboard/index.js +0 -374
- package/src/web/public/js/onboard/publishHandler.js +0 -172
- package/src/web/public/js/onboard/state.js +0 -76
- package/src/web/public/js/onboard/templates.js +0 -342
- package/src/web/public/js/onboard/uiHandlers.js +0 -1076
- package/src/web/public/js/onboard/validationHandlers.js +0 -493
- package/src/web/public/js/serverCategoryDetails.js +0 -364
- package/src/web/public/js/serverCategoryList.js +0 -241
- package/src/web/public/js/settings.js +0 -314
- package/src/web/public/modal.html +0 -84
- package/src/web/public/onboard.html +0 -296
- package/src/web/public/settings.html +0 -135
- package/src/web/public/styles.css +0 -277
- package/src/web/server.ts +0 -478
- package/tsconfig.json +0 -18
- package/wiki/Installation.md +0 -3
- package/wiki/Publish.md +0 -3
|
@@ -1,241 +1,2 @@
|
|
|
1
|
-
import { allServerCategoriesData
|
|
2
|
-
|
|
3
|
-
import { showToast } from './notifications.js';
|
|
4
|
-
import { buildUrlWithFlights } from './flights/flights.js';
|
|
5
|
-
|
|
6
|
-
// Store state for categories
|
|
7
|
-
let pinnedCategories = {};
|
|
8
|
-
|
|
9
|
-
// Wait for data to be loaded
|
|
10
|
-
async function waitForData() {
|
|
11
|
-
if (allServerCategoriesData && allServerCategoriesData.length > 0) {
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
await fetchServerCategories();
|
|
15
|
-
return allServerCategoriesData && allServerCategoriesData.length > 0;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Load pinned state from localStorage
|
|
19
|
-
function loadPinnedState() {
|
|
20
|
-
const saved = localStorage.getItem('pinnedCategories');
|
|
21
|
-
if (saved) {
|
|
22
|
-
try {
|
|
23
|
-
pinnedCategories = JSON.parse(saved);
|
|
24
|
-
} catch (e) {
|
|
25
|
-
console.error('Error parsing pinned categories from localStorage:', e);
|
|
26
|
-
pinnedCategories = {};
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Save pinned state to localStorage
|
|
32
|
-
function savePinnedState() {
|
|
33
|
-
localStorage.setItem('pinnedCategories', JSON.stringify(pinnedCategories));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Toggle pin status for a category
|
|
37
|
-
function togglePinCategory(categoryName, event) {
|
|
38
|
-
// Stop propagation to prevent navigation
|
|
39
|
-
event.stopPropagation();
|
|
40
|
-
|
|
41
|
-
// Toggle pin status
|
|
42
|
-
if (pinnedCategories[categoryName]) {
|
|
43
|
-
delete pinnedCategories[categoryName];
|
|
44
|
-
} else {
|
|
45
|
-
pinnedCategories[categoryName] = true;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Save to localStorage
|
|
49
|
-
savePinnedState();
|
|
50
|
-
|
|
51
|
-
// Find the server item element and add a temporary animation class
|
|
52
|
-
const serverItem = event.target.closest('.server-item');
|
|
53
|
-
if (serverItem) {
|
|
54
|
-
serverItem.classList.add('pin-animation');
|
|
55
|
-
setTimeout(() => {
|
|
56
|
-
serverItem.classList.remove('pin-animation');
|
|
57
|
-
}, 300); // Animation duration
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Re-render list with updated pin status
|
|
61
|
-
if (allServerCategoriesData && allServerCategoriesData.length > 0) {
|
|
62
|
-
renderServerCategoryList(allServerCategoriesData);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Function to show the last selected category on page load
|
|
67
|
-
async function loadLastSelectedCategory() {
|
|
68
|
-
const lastSelected = localStorage.getItem('lastSelectedCategory');
|
|
69
|
-
if (lastSelected && allServerCategoriesData) {
|
|
70
|
-
// Check if the category still exists
|
|
71
|
-
const categoryExists = allServerCategoriesData.some(server => server.name === lastSelected);
|
|
72
|
-
if (categoryExists) {
|
|
73
|
-
await showServerDetails(lastSelected);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Function to render server list (used by fetchServerCategories and search)
|
|
79
|
-
function renderServerCategoryList(servers) {
|
|
80
|
-
const serverCategoryList = document.getElementById('serverCategoryList');
|
|
81
|
-
|
|
82
|
-
if (servers.length === 0) {
|
|
83
|
-
serverCategoryList.innerHTML = '<p class="text-gray-500">No servers found matching search.</p>';
|
|
84
|
-
document.getElementById('serverCategoryDetails').innerHTML = '<p>Select a server from the list to see details.</p>'; // Clear details
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Load pinned state
|
|
89
|
-
loadPinnedState();
|
|
90
|
-
|
|
91
|
-
// Create a copy of the servers array to avoid modifying the original
|
|
92
|
-
const sortedServers = [...servers];
|
|
93
|
-
|
|
94
|
-
// Sort servers with pinned ones at the top
|
|
95
|
-
sortedServers.sort((a, b) => {
|
|
96
|
-
const isPinnedA = pinnedCategories[a.name] === true;
|
|
97
|
-
const isPinnedB = pinnedCategories[b.name] === true;
|
|
98
|
-
|
|
99
|
-
if (isPinnedA && !isPinnedB) return -1;
|
|
100
|
-
if (!isPinnedA && isPinnedB) return 1;
|
|
101
|
-
return 0; // Keep original order for items with same pin status
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
serverCategoryList.innerHTML = sortedServers.map(server => {
|
|
105
|
-
let statusHtml = '';
|
|
106
|
-
|
|
107
|
-
// Add tool status summary if available
|
|
108
|
-
if (server.installationStatus && server.installationStatus.serversStatus) {
|
|
109
|
-
const totalTools = Object.keys(server.installationStatus.serversStatus).length;
|
|
110
|
-
const installedTools = Object.values(server.installationStatus.serversStatus)
|
|
111
|
-
.filter(serverStatus =>
|
|
112
|
-
Object.values(serverStatus.installedStatus || {})
|
|
113
|
-
.some(opStatus => opStatus.status === 'completed' && opStatus.type === 'install')
|
|
114
|
-
).length;
|
|
115
|
-
|
|
116
|
-
if (totalTools > 0) {
|
|
117
|
-
let colorClass, icon, statusText;
|
|
118
|
-
|
|
119
|
-
if (installedTools === totalTools) {
|
|
120
|
-
// All tools installed
|
|
121
|
-
colorClass = "text-green-600 bg-green-50";
|
|
122
|
-
icon = '<svg class="w-5 h-5 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>';
|
|
123
|
-
statusText = "Fully Configured";
|
|
124
|
-
} else if (installedTools > 0) {
|
|
125
|
-
// Some tools installed
|
|
126
|
-
colorClass = "text-green-600 bg-green-50";
|
|
127
|
-
icon = '<svg class="w-5 h-5 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>';
|
|
128
|
-
statusText = "Partial Configured";
|
|
129
|
-
} else {
|
|
130
|
-
// No tools installed
|
|
131
|
-
colorClass = "text-yellow-600 bg-orange-50";
|
|
132
|
-
icon = '<svg class="w-5 h-5 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM10 13a1 1 0 110-2 1 1 0 010 2zm-1.75-5.75a.75.75 0 00-1.5 0v3a.75.75 0 001.5 0v-3z" clip-rule="evenodd" /></svg>';
|
|
133
|
-
statusText = "Not Configured";
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
statusHtml = `<span class="${colorClass} inline-flex items-center px-2 py-1 rounded-full text-xs" style="width: fit-content; max-width: 100%;">
|
|
137
|
-
${icon}<span class="truncate">${statusText} (${installedTools}/${totalTools})</span>
|
|
138
|
-
</span>`;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
let systemTagsHtml = '';
|
|
143
|
-
if (server.feedConfiguration?.systemTags && Object.keys(server.feedConfiguration.systemTags).length > 0) {
|
|
144
|
-
systemTagsHtml += '<div class="flex flex-wrap gap-1 ml-2">'; // Added ml-2 for spacing
|
|
145
|
-
for (const [key, value] of Object.entries(server.feedConfiguration.systemTags)) {
|
|
146
|
-
systemTagsHtml += `<span class="text-xs bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full">${key}: ${value}</span>`;
|
|
147
|
-
}
|
|
148
|
-
systemTagsHtml += '</div>';
|
|
149
|
-
}
|
|
150
|
-
// Check if this server is pinned
|
|
151
|
-
const isPinned = pinnedCategories[server.name] === true;
|
|
152
|
-
const pinnedClass = isPinned ? 'pinned' : '';
|
|
153
|
-
|
|
154
|
-
// Pin/unpin button with appropriate icon
|
|
155
|
-
const pinIcon = isPinned
|
|
156
|
-
? '<i class="bx bxs-pin"></i>' // Solid pin icon for pinned items
|
|
157
|
-
: '<i class="bx bx-pin"></i>'; // Outline pin icon for unpinned items
|
|
158
|
-
|
|
159
|
-
return `
|
|
160
|
-
<div class="server-item border border-gray-200 p-3 rounded hover:bg-gray-50 cursor-pointer transition duration-150 ease-in-out ${pinnedClass}"
|
|
161
|
-
data-server-name="${server.name}" onclick="navigateToCategory('${server.name}')">
|
|
162
|
-
<div class="flex justify-between items-center">
|
|
163
|
-
<h3 class="font-semibold text-gray-800">${server.displayName || server.name}</h3>
|
|
164
|
-
<div class="flex items-center">
|
|
165
|
-
<div class="pin-button ${pinnedClass}" onclick="togglePinCategoryItem('${server.name}', event)" title="${isPinned ? 'Unpin' : 'Pin'} this category">
|
|
166
|
-
${pinIcon}
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
</div>
|
|
170
|
-
<div class="text-sm text-gray-500 flex items-center mt-1">
|
|
171
|
-
${statusHtml}
|
|
172
|
-
${systemTagsHtml}
|
|
173
|
-
</div>
|
|
174
|
-
</div>
|
|
175
|
-
`;
|
|
176
|
-
}).join('');
|
|
177
|
-
|
|
178
|
-
// After rendering, try to load the last selected category
|
|
179
|
-
loadLastSelectedCategory();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Setup search functionality
|
|
183
|
-
function setupSearch() {
|
|
184
|
-
const searchBox = document.getElementById('searchBox');
|
|
185
|
-
|
|
186
|
-
// Load pinned state on page load
|
|
187
|
-
loadPinnedState();
|
|
188
|
-
|
|
189
|
-
searchBox.addEventListener('input', async function () {
|
|
190
|
-
const searchTerm = this.value.toLowerCase();
|
|
191
|
-
|
|
192
|
-
try {
|
|
193
|
-
// Ensure data is loaded
|
|
194
|
-
if (!(await waitForData())) {
|
|
195
|
-
showToast('Error: Unable to load server data', 'error');
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Filter the servers list based on search
|
|
200
|
-
const filteredServers = allServerCategoriesData.filter(server => {
|
|
201
|
-
// Check category name/display name
|
|
202
|
-
const categoryMatch = (server.displayName || server.name).toLowerCase().includes(searchTerm);
|
|
203
|
-
|
|
204
|
-
// Check description
|
|
205
|
-
const descriptionMatch = (server.description || '').toLowerCase().includes(searchTerm);
|
|
206
|
-
|
|
207
|
-
// Check MCP server names from feedConfiguration
|
|
208
|
-
const mcpServerMatch = server.feedConfiguration?.mcpServers?.some(mcpServer =>
|
|
209
|
-
(mcpServer.displayName || mcpServer.name).toLowerCase().includes(searchTerm)
|
|
210
|
-
) || false;
|
|
211
|
-
|
|
212
|
-
// Check installed server names from installationStatus
|
|
213
|
-
const installedServerMatch = server.installationStatus?.serversStatus &&
|
|
214
|
-
Object.keys(server.installationStatus.serversStatus)
|
|
215
|
-
.some(serverName => serverName.toLowerCase().includes(searchTerm));
|
|
216
|
-
|
|
217
|
-
return categoryMatch || descriptionMatch || mcpServerMatch || installedServerMatch;
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
renderServerCategoryList(filteredServers);
|
|
221
|
-
} catch (error) {
|
|
222
|
-
console.error('Error in search:', error);
|
|
223
|
-
showToast('Error performing search', 'error');
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function navigateToCategory(categoryName) {
|
|
229
|
-
// This function will now handle the navigation, ensuring flight params are preserved.
|
|
230
|
-
// It constructs the new URL with the selected category and existing flight params.
|
|
231
|
-
window.location.href = buildUrlWithFlights('index.html', { category: categoryName });
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Make navigateToCategory globally accessible if it's directly used in HTML onclick
|
|
235
|
-
// Alternatively, attach event listeners dynamically after rendering the list.
|
|
236
|
-
// For simplicity with current structure, we'll make it global.
|
|
237
|
-
window.navigateToCategory = navigateToCategory;
|
|
238
|
-
window.togglePinCategoryItem = togglePinCategory;
|
|
239
|
-
|
|
240
|
-
// Export functions
|
|
241
|
-
export { renderServerCategoryList, setupSearch, loadLastSelectedCategory, togglePinCategory };
|
|
1
|
+
import{allServerCategoriesData,fetchServerCategories}from"./api.js";import{showServerDetails}from"./serverCategoryDetails.js";import{showToast}from"./notifications.js";import{buildUrlWithFlights}from"./flights/flights.js";let pinnedCategories={};async function waitForData(){return!!(allServerCategoriesData&&allServerCategoriesData.length>0)||(await fetchServerCategories(),allServerCategoriesData&&allServerCategoriesData.length>0)}function loadPinnedState(){const e=localStorage.getItem("pinnedCategories");if(e)try{pinnedCategories=JSON.parse(e)}catch(e){pinnedCategories={}}}function savePinnedState(){localStorage.setItem("pinnedCategories",JSON.stringify(pinnedCategories))}function togglePinCategory(e,t){t.stopPropagation(),pinnedCategories[e]?delete pinnedCategories[e]:pinnedCategories[e]=!0,savePinnedState();const a=t.target.closest(".server-item");a&&(a.classList.add("pin-animation"),setTimeout((()=>{a.classList.remove("pin-animation")}),300)),allServerCategoriesData&&allServerCategoriesData.length>0&&renderServerCategoryList(allServerCategoriesData)}async function loadLastSelectedCategory(){const e=localStorage.getItem("lastSelectedCategory");e&&allServerCategoriesData&&allServerCategoriesData.some((t=>t.name===e))&&await showServerDetails(e)}function renderServerCategoryList(e){const t=document.getElementById("serverCategoryList");if(0===e.length)return t.innerHTML='<p class="text-gray-500">No servers found matching search.</p>',void(document.getElementById("serverCategoryDetails").innerHTML="<p>Select a server from the list to see details.</p>");loadPinnedState();const a=[...e];a.sort(((e,t)=>{const a=!0===pinnedCategories[e.name],r=!0===pinnedCategories[t.name];return a&&!r?-1:!a&&r?1:0})),t.innerHTML=a.map((e=>{let t="";if(e.installationStatus&&e.installationStatus.serversStatus){const a=Object.keys(e.installationStatus.serversStatus).length,r=Object.values(e.installationStatus.serversStatus).filter((e=>Object.values(e.installedStatus||{}).some((e=>"completed"===e.status&&"install"===e.type)))).length;if(a>0){let e,n,s;r===a?(e="text-green-600 bg-green-50",n='<svg class="w-5 h-5 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>',s="Fully Configured"):r>0?(e="text-green-600 bg-green-50",n='<svg class="w-5 h-5 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>',s="Partial Configured"):(e="text-yellow-600 bg-orange-50",n='<svg class="w-5 h-5 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM10 13a1 1 0 110-2 1 1 0 010 2zm-1.75-5.75a.75.75 0 00-1.5 0v3a.75.75 0 001.5 0v-3z" clip-rule="evenodd" /></svg>',s="Not Configured"),t=`<span class="${e} inline-flex items-center px-2 py-1 rounded-full text-xs" style="width: fit-content; max-width: 100%;">\n ${n}<span class="truncate">${s} (${r}/${a})</span>\n </span>`}}let a="";if(e.feedConfiguration?.systemTags&&Object.keys(e.feedConfiguration.systemTags).length>0){a+='<div class="flex flex-wrap gap-1 ml-2">';for(const[t,r]of Object.entries(e.feedConfiguration.systemTags))a+=`<span class="text-xs bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full">${t}: ${r}</span>`;a+="</div>"}const r=!0===pinnedCategories[e.name],n=r?"pinned":"",s=r?'<i class="bx bxs-pin"></i>':'<i class="bx bx-pin"></i>';return`\n <div class="server-item border border-gray-200 p-3 rounded hover:bg-gray-50 cursor-pointer transition duration-150 ease-in-out ${n}"\n data-server-name="${e.name}" onclick="navigateToCategory('${e.name}')">\n <div class="flex justify-between items-center">\n <h3 class="font-semibold text-gray-800">${e.displayName||e.name}</h3>\n <div class="flex items-center">\n <div class="pin-button ${n}" onclick="togglePinCategoryItem('${e.name}', event)" title="${r?"Unpin":"Pin"} this category">\n ${s}\n </div>\n </div>\n </div>\n <div class="text-sm text-gray-500 flex items-center mt-1">\n ${t}\n ${a}\n </div>\n </div>\n `})).join(""),loadLastSelectedCategory()}function setupSearch(){const e=document.getElementById("searchBox");loadPinnedState(),e.addEventListener("input",(async function(){const e=this.value.toLowerCase();try{if(!await waitForData())return void showToast("Error: Unable to load server data","error");renderServerCategoryList(allServerCategoriesData.filter((t=>{const a=(t.displayName||t.name).toLowerCase().includes(e),r=(t.description||"").toLowerCase().includes(e),n=t.feedConfiguration?.mcpServers?.some((t=>(t.displayName||t.name).toLowerCase().includes(e)))||!1,s=t.installationStatus?.serversStatus&&Object.keys(t.installationStatus.serversStatus).some((t=>t.toLowerCase().includes(e)));return a||r||n||s})))}catch(e){showToast("Error performing search","error")}}))}function navigateToCategory(e){window.location.href=buildUrlWithFlights("index.html",{category:e})}window.navigateToCategory=navigateToCategory,window.togglePinCategoryItem=togglePinCategory;export{renderServerCategoryList,setupSearch,loadLastSelectedCategory,togglePinCategory};
|
|
2
|
+
//# sourceMappingURL=serverCategoryList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["allServerCategoriesData","fetchServerCategories"],"sources":["0"],"sourcesContent":["import { allServerCategoriesData, fetchServerCategories } from './api.js';\r\nimport { showServerDetails } from './serverCategoryDetails.js'; // Still needed for initial load and direct calls\r\nimport { showToast } from './notifications.js';\r\nimport { buildUrlWithFlights } from './flights/flights.js';\r\n\r\n// Store state for categories\r\nlet pinnedCategories = {};\r\n\r\n// Wait for data to be loaded\r\nasync function waitForData() {\r\n if (allServerCategoriesData && allServerCategoriesData.length > 0) {\r\n return true;\r\n }\r\n await fetchServerCategories();\r\n return allServerCategoriesData && allServerCategoriesData.length > 0;\r\n}\r\n\r\n// Load pinned state from localStorage\r\nfunction loadPinnedState() {\r\n const saved = localStorage.getItem('pinnedCategories');\r\n if (saved) {\r\n try {\r\n pinnedCategories = JSON.parse(saved);\r\n } catch (e) {\r\n console.error('Error parsing pinned categories from localStorage:', e);\r\n pinnedCategories = {};\r\n }\r\n }\r\n}\r\n\r\n// Save pinned state to localStorage\r\nfunction savePinnedState() {\r\n localStorage.setItem('pinnedCategories', JSON.stringify(pinnedCategories));\r\n}\r\n\r\n// Toggle pin status for a category\r\nfunction togglePinCategory(categoryName, event) {\r\n // Stop propagation to prevent navigation\r\n event.stopPropagation();\r\n \r\n // Toggle pin status\r\n if (pinnedCategories[categoryName]) {\r\n delete pinnedCategories[categoryName];\r\n } else {\r\n pinnedCategories[categoryName] = true;\r\n }\r\n \r\n // Save to localStorage\r\n savePinnedState();\r\n \r\n // Find the server item element and add a temporary animation class\r\n const serverItem = event.target.closest('.server-item');\r\n if (serverItem) {\r\n serverItem.classList.add('pin-animation');\r\n setTimeout(() => {\r\n serverItem.classList.remove('pin-animation');\r\n }, 300); // Animation duration\r\n }\r\n \r\n // Re-render list with updated pin status\r\n if (allServerCategoriesData && allServerCategoriesData.length > 0) {\r\n renderServerCategoryList(allServerCategoriesData);\r\n }\r\n}\r\n\r\n// Function to show the last selected category on page load\r\nasync function loadLastSelectedCategory() {\r\n const lastSelected = localStorage.getItem('lastSelectedCategory');\r\n if (lastSelected && allServerCategoriesData) {\r\n // Check if the category still exists\r\n const categoryExists = allServerCategoriesData.some(server => server.name === lastSelected);\r\n if (categoryExists) {\r\n await showServerDetails(lastSelected);\r\n }\r\n }\r\n}\r\n\r\n// Function to render server list (used by fetchServerCategories and search)\r\nfunction renderServerCategoryList(servers) {\r\n const serverCategoryList = document.getElementById('serverCategoryList');\r\n\r\n if (servers.length === 0) {\r\n serverCategoryList.innerHTML = '<p class=\"text-gray-500\">No servers found matching search.</p>';\r\n document.getElementById('serverCategoryDetails').innerHTML = '<p>Select a server from the list to see details.</p>'; // Clear details\r\n return;\r\n }\r\n \r\n // Load pinned state\r\n loadPinnedState();\r\n \r\n // Create a copy of the servers array to avoid modifying the original\r\n const sortedServers = [...servers];\r\n \r\n // Sort servers with pinned ones at the top\r\n sortedServers.sort((a, b) => {\r\n const isPinnedA = pinnedCategories[a.name] === true;\r\n const isPinnedB = pinnedCategories[b.name] === true;\r\n \r\n if (isPinnedA && !isPinnedB) return -1;\r\n if (!isPinnedA && isPinnedB) return 1;\r\n return 0; // Keep original order for items with same pin status\r\n });\r\n\r\n serverCategoryList.innerHTML = sortedServers.map(server => {\r\n let statusHtml = '';\r\n\r\n // Add tool status summary if available\r\n if (server.installationStatus && server.installationStatus.serversStatus) {\r\n const totalTools = Object.keys(server.installationStatus.serversStatus).length;\r\n const installedTools = Object.values(server.installationStatus.serversStatus)\r\n .filter(serverStatus =>\r\n Object.values(serverStatus.installedStatus || {})\r\n .some(opStatus => opStatus.status === 'completed' && opStatus.type === 'install')\r\n ).length;\r\n\r\n if (totalTools > 0) {\r\n let colorClass, icon, statusText;\r\n\r\n if (installedTools === totalTools) {\r\n // All tools installed\r\n colorClass = \"text-green-600 bg-green-50\";\r\n icon = '<svg class=\"w-5 h-5 mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\" clip-rule=\"evenodd\"></path></svg>';\r\n statusText = \"Fully Configured\";\r\n } else if (installedTools > 0) {\r\n // Some tools installed\r\n colorClass = \"text-green-600 bg-green-50\";\r\n icon = '<svg class=\"w-5 h-5 mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\" clip-rule=\"evenodd\"></path></svg>';\r\n statusText = \"Partial Configured\";\r\n } else {\r\n // No tools installed\r\n colorClass = \"text-yellow-600 bg-orange-50\";\r\n icon = '<svg class=\"w-5 h-5 mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path fill-rule=\"evenodd\" d=\"M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM10 13a1 1 0 110-2 1 1 0 010 2zm-1.75-5.75a.75.75 0 00-1.5 0v3a.75.75 0 001.5 0v-3z\" clip-rule=\"evenodd\" /></svg>';\r\n statusText = \"Not Configured\";\r\n }\r\n\r\n statusHtml = `<span class=\"${colorClass} inline-flex items-center px-2 py-1 rounded-full text-xs\" style=\"width: fit-content; max-width: 100%;\">\r\n ${icon}<span class=\"truncate\">${statusText} (${installedTools}/${totalTools})</span>\r\n </span>`;\r\n }\r\n }\r\n\r\n let systemTagsHtml = '';\r\n if (server.feedConfiguration?.systemTags && Object.keys(server.feedConfiguration.systemTags).length > 0) {\r\n systemTagsHtml += '<div class=\"flex flex-wrap gap-1 ml-2\">'; // Added ml-2 for spacing\r\n for (const [key, value] of Object.entries(server.feedConfiguration.systemTags)) {\r\n systemTagsHtml += `<span class=\"text-xs bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full\">${key}: ${value}</span>`;\r\n }\r\n systemTagsHtml += '</div>';\r\n }\r\n // Check if this server is pinned\r\n const isPinned = pinnedCategories[server.name] === true;\r\n const pinnedClass = isPinned ? 'pinned' : '';\r\n \r\n // Pin/unpin button with appropriate icon\r\n const pinIcon = isPinned \r\n ? '<i class=\"bx bxs-pin\"></i>' // Solid pin icon for pinned items\r\n : '<i class=\"bx bx-pin\"></i>'; // Outline pin icon for unpinned items\r\n\r\n return `\r\n <div class=\"server-item border border-gray-200 p-3 rounded hover:bg-gray-50 cursor-pointer transition duration-150 ease-in-out ${pinnedClass}\"\r\n data-server-name=\"${server.name}\" onclick=\"navigateToCategory('${server.name}')\">\r\n <div class=\"flex justify-between items-center\">\r\n <h3 class=\"font-semibold text-gray-800\">${server.displayName || server.name}</h3>\r\n <div class=\"flex items-center\">\r\n <div class=\"pin-button ${pinnedClass}\" onclick=\"togglePinCategoryItem('${server.name}', event)\" title=\"${isPinned ? 'Unpin' : 'Pin'} this category\">\r\n ${pinIcon}\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"text-sm text-gray-500 flex items-center mt-1\">\r\n ${statusHtml}\r\n ${systemTagsHtml}\r\n </div>\r\n </div>\r\n `;\r\n }).join('');\r\n\r\n // After rendering, try to load the last selected category\r\n loadLastSelectedCategory();\r\n}\r\n\r\n// Setup search functionality\r\nfunction setupSearch() {\r\n const searchBox = document.getElementById('searchBox');\r\n \r\n // Load pinned state on page load\r\n loadPinnedState();\r\n\r\n searchBox.addEventListener('input', async function () {\r\n const searchTerm = this.value.toLowerCase();\r\n\r\n try {\r\n // Ensure data is loaded\r\n if (!(await waitForData())) {\r\n showToast('Error: Unable to load server data', 'error');\r\n return;\r\n }\r\n\r\n // Filter the servers list based on search\r\n const filteredServers = allServerCategoriesData.filter(server => {\r\n // Check category name/display name\r\n const categoryMatch = (server.displayName || server.name).toLowerCase().includes(searchTerm);\r\n\r\n // Check description\r\n const descriptionMatch = (server.description || '').toLowerCase().includes(searchTerm);\r\n\r\n // Check MCP server names from feedConfiguration\r\n const mcpServerMatch = server.feedConfiguration?.mcpServers?.some(mcpServer =>\r\n (mcpServer.displayName || mcpServer.name).toLowerCase().includes(searchTerm)\r\n ) || false;\r\n\r\n // Check installed server names from installationStatus\r\n const installedServerMatch = server.installationStatus?.serversStatus &&\r\n Object.keys(server.installationStatus.serversStatus)\r\n .some(serverName => serverName.toLowerCase().includes(searchTerm));\r\n\r\n return categoryMatch || descriptionMatch || mcpServerMatch || installedServerMatch;\r\n });\r\n\r\n renderServerCategoryList(filteredServers);\r\n } catch (error) {\r\n console.error('Error in search:', error);\r\n showToast('Error performing search', 'error');\r\n }\r\n });\r\n}\r\n\r\nfunction navigateToCategory(categoryName) {\r\n // This function will now handle the navigation, ensuring flight params are preserved.\r\n // It constructs the new URL with the selected category and existing flight params.\r\n window.location.href = buildUrlWithFlights('index.html', { category: categoryName });\r\n}\r\n\r\n// Make navigateToCategory globally accessible if it's directly used in HTML onclick\r\n// Alternatively, attach event listeners dynamically after rendering the list.\r\n// For simplicity with current structure, we'll make it global.\r\nwindow.navigateToCategory = navigateToCategory;\r\nwindow.togglePinCategoryItem = togglePinCategory;\r\n\r\n// Export functions\r\nexport { renderServerCategoryList, setupSearch, loadLastSelectedCategory, togglePinCategory };"],"mappings":"OAASA,wBAAyBC,0BAA6B,kB","ignoreList":[]}
|
|
@@ -1,314 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
// Toast notification function
|
|
4
|
-
function showToast(message, type = 'success') {
|
|
5
|
-
const toastContainer = document.querySelector('.toast-container');
|
|
6
|
-
if (!toastContainer) return;
|
|
7
|
-
|
|
8
|
-
const toastId = `toast-${Date.now()}`;
|
|
9
|
-
const toastHTML = `
|
|
10
|
-
<div id="${toastId}" class="toast align-items-center text-white bg-${type === 'success' ? 'success' : 'danger'} border-0" role="alert" aria-live="assertive" aria-atomic="true">
|
|
11
|
-
<div class="d-flex">
|
|
12
|
-
<div class="toast-body">
|
|
13
|
-
${message}
|
|
14
|
-
</div>
|
|
15
|
-
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
18
|
-
`;
|
|
19
|
-
toastContainer.insertAdjacentHTML('beforeend', toastHTML);
|
|
20
|
-
const toastElement = document.getElementById(toastId);
|
|
21
|
-
const toast = new bootstrap.Toast(toastElement, { delay: 3000 });
|
|
22
|
-
toast.show();
|
|
23
|
-
toastElement.addEventListener('hidden.bs.toast', () => {
|
|
24
|
-
toastElement.remove();
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
29
|
-
// All DOM element queries and event listeners must be inside this block!
|
|
30
|
-
const settingsForm = document.getElementById('settingsForm');
|
|
31
|
-
const nodePathInput = document.getElementById('nodePath');
|
|
32
|
-
const browserPathInput = document.getElementById('browserPath');
|
|
33
|
-
const systemEnvironmentsDiv = document.getElementById('systemEnvironments');
|
|
34
|
-
|
|
35
|
-
// Python Environments elements
|
|
36
|
-
const pythonEnvsContainer = document.getElementById('pythonEnvsContainer');
|
|
37
|
-
const addPythonEnvButton = document.getElementById('addPythonEnvButton');
|
|
38
|
-
const pythonEnvsLoadingMsg = document.getElementById('pythonEnvsLoadingMsg');
|
|
39
|
-
|
|
40
|
-
// User Configurations elements
|
|
41
|
-
const userConfigurationsContainer = document.getElementById('userConfigurationsContainer');
|
|
42
|
-
const addUserConfigButton = document.getElementById('addUserConfigButton');
|
|
43
|
-
const userConfigLoadingMsg = document.getElementById('userConfigLoadingMsg');
|
|
44
|
-
|
|
45
|
-
const cancelButton = document.getElementById('cancelButton');
|
|
46
|
-
const setupButton = document.getElementById('setupButton');
|
|
47
|
-
|
|
48
|
-
// (Removed duplicate showToast function here)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
async function loadSettings() {
|
|
52
|
-
try {
|
|
53
|
-
const response = await fetch('/api/settings');
|
|
54
|
-
if (!response.ok) {
|
|
55
|
-
const errorData = await response.json();
|
|
56
|
-
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
|
|
57
|
-
}
|
|
58
|
-
const apiResponse = await response.json();
|
|
59
|
-
if (apiResponse.success && apiResponse.data) {
|
|
60
|
-
const settings = apiResponse.data;
|
|
61
|
-
|
|
62
|
-
// Populate Python Environments
|
|
63
|
-
if(pythonEnvsLoadingMsg) pythonEnvsLoadingMsg.remove(); // Remove loading message
|
|
64
|
-
pythonEnvsContainer.innerHTML = ''; // Clear previous entries
|
|
65
|
-
|
|
66
|
-
if (settings.pythonEnvs && Object.keys(settings.pythonEnvs).length > 0) {
|
|
67
|
-
Object.entries(settings.pythonEnvs).forEach(([server, path]) => {
|
|
68
|
-
renderPythonEnvInput(server, path);
|
|
69
|
-
});
|
|
70
|
-
} else if (settings.pythonEnv) {
|
|
71
|
-
// Handle legacy pythonEnv
|
|
72
|
-
renderPythonEnvInput('system', settings.pythonEnv);
|
|
73
|
-
} else {
|
|
74
|
-
if (!pythonEnvsContainer.querySelector('.python-env-row')) { // Add placeholder if no rows exist
|
|
75
|
-
const p = document.createElement('p');
|
|
76
|
-
p.className = 'text-gray-500 python-env-placeholder';
|
|
77
|
-
p.textContent = 'No Python environments defined. Click "Add Python Environment" to add one.';
|
|
78
|
-
pythonEnvsContainer.appendChild(p);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
nodePathInput.value = settings.nodePath || '';
|
|
83
|
-
browserPathInput.value = settings.browserPath || '';
|
|
84
|
-
|
|
85
|
-
// Populate System Environments
|
|
86
|
-
systemEnvironmentsDiv.innerHTML = ''; // Clear loading message
|
|
87
|
-
if (settings.systemEnvironments && Object.keys(settings.systemEnvironments).length > 0) {
|
|
88
|
-
// Create a modern, scrollable list (not a table)
|
|
89
|
-
const list = document.createElement('div');
|
|
90
|
-
list.className = 'flex flex-col';
|
|
91
|
-
|
|
92
|
-
Object.entries(settings.systemEnvironments).forEach(([key, value], idx) => {
|
|
93
|
-
const item = document.createElement('div');
|
|
94
|
-
item.className =
|
|
95
|
-
'flex flex-col sm:flex-row sm:items-center bg-white rounded-md border border-gray-200 px-4 py-2 shadow-sm hover:shadow transition group';
|
|
96
|
-
item.innerHTML = `
|
|
97
|
-
<div class="flex items-center min-w-0 w-full sm:w-1/4 sm:mb-0">
|
|
98
|
-
<i class='bx bx-cog text-blue-400 mr-2'></i>
|
|
99
|
-
<span class="font-mono font-semibold text-gray-800 text-base truncate" title="${key}">${key}</span>
|
|
100
|
-
</div>
|
|
101
|
-
<div class="flex-1 min-w-0 break-all text-gray-700 text-sm pl-7 sm:pl-4">
|
|
102
|
-
${value}
|
|
103
|
-
</div>
|
|
104
|
-
`;
|
|
105
|
-
list.appendChild(item);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// Restore frame styling for the new layout
|
|
109
|
-
systemEnvironmentsDiv.classList.remove('shadow');
|
|
110
|
-
systemEnvironmentsDiv.classList.add('rounded-lg', 'bg-white', 'border', 'border-gray-200', 'border-1');
|
|
111
|
-
systemEnvironmentsDiv.classList.remove('border-2', 'border-4', 'border-8');
|
|
112
|
-
systemEnvironmentsDiv.classList.add('p-0');
|
|
113
|
-
systemEnvironmentsDiv.style.padding = '0.5rem 0.5rem 0.5rem 0.5rem';
|
|
114
|
-
systemEnvironmentsDiv.appendChild(list);
|
|
115
|
-
} else {
|
|
116
|
-
systemEnvironmentsDiv.innerHTML = '<p class="text-gray-500">No system environment variables loaded or available.</p>';
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Populate User Configurations
|
|
120
|
-
if(userConfigLoadingMsg) userConfigLoadingMsg.remove(); // Remove loading message
|
|
121
|
-
userConfigurationsContainer.innerHTML = ''; // Clear previous entries
|
|
122
|
-
if (settings.userConfigurations && Object.keys(settings.userConfigurations).length > 0) {
|
|
123
|
-
Object.entries(settings.userConfigurations).forEach(([key, value]) => {
|
|
124
|
-
renderUserConfigInput(key, value);
|
|
125
|
-
});
|
|
126
|
-
} else {
|
|
127
|
-
if (!userConfigurationsContainer.querySelector('.user-config-row')) { // Add placeholder if no rows exist
|
|
128
|
-
const p = document.createElement('p');
|
|
129
|
-
p.className = 'text-gray-500 user-config-placeholder';
|
|
130
|
-
p.textContent = 'No user configurations defined. Click "Add User Configuration" to add one.';
|
|
131
|
-
userConfigurationsContainer.appendChild(p);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
} else {
|
|
136
|
-
throw new Error(apiResponse.error || 'Failed to load settings.');
|
|
137
|
-
}
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error('Error loading settings:', error);
|
|
140
|
-
showToast(`Error loading settings: ${error.message}`, 'danger');
|
|
141
|
-
systemEnvironmentsDiv.innerHTML = `<p class="text-red-500">Error loading environment variables: ${error.message}</p>`;
|
|
142
|
-
if(userConfigLoadingMsg) userConfigLoadingMsg.textContent = `Error loading user configurations: ${error.message}`;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function renderPythonEnvInput(server = 'system', path = '') {
|
|
147
|
-
const placeholder = pythonEnvsContainer.querySelector('.python-env-placeholder');
|
|
148
|
-
if (placeholder) placeholder.remove();
|
|
149
|
-
|
|
150
|
-
const uniqueId = `python-env-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
|
|
151
|
-
const div = document.createElement('div');
|
|
152
|
-
div.className = 'python-env-row flex items-center gap-3 mb-2';
|
|
153
|
-
div.innerHTML = `
|
|
154
|
-
<input type="text" value="${server}" class="form-input form-control w-1/3 px-3 py-2 rounded-md python-env-server" placeholder="Server Name (e.g., system)">
|
|
155
|
-
<input type="text" value="${path}" class="form-input form-control w-2/3 px-3 py-2 rounded-md python-env-path" placeholder="e.g., /usr/bin/python3 or C:/Python39/python.exe">
|
|
156
|
-
<button type="button" class="btn btn-danger btn-sm remove-python-env-button flex items-center">
|
|
157
|
-
<i class='bx bx-trash'></i>
|
|
158
|
-
</button>
|
|
159
|
-
`;
|
|
160
|
-
pythonEnvsContainer.appendChild(div);
|
|
161
|
-
div.querySelector('.remove-python-env-button').addEventListener('click', function() {
|
|
162
|
-
this.closest('.python-env-row').remove();
|
|
163
|
-
if (!pythonEnvsContainer.querySelector('.python-env-row')) { // Add placeholder if no rows exist after removal
|
|
164
|
-
const p = document.createElement('p');
|
|
165
|
-
p.className = 'text-gray-500 python-env-placeholder';
|
|
166
|
-
p.textContent = 'No Python environments defined. Click "Add Python Environment" to add one.';
|
|
167
|
-
pythonEnvsContainer.appendChild(p);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function renderUserConfigInput(key = '', value = '') {
|
|
173
|
-
const placeholder = userConfigurationsContainer.querySelector('.user-config-placeholder');
|
|
174
|
-
if (placeholder) placeholder.remove();
|
|
175
|
-
|
|
176
|
-
const uniqueId = `user-config-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
|
|
177
|
-
const div = document.createElement('div');
|
|
178
|
-
div.className = 'user-config-row flex items-center gap-3 mb-2';
|
|
179
|
-
const isSecret = key.toLowerCase().includes('key');
|
|
180
|
-
const valueInputType = isSecret ? 'password' : 'text';
|
|
181
|
-
|
|
182
|
-
// Key Input
|
|
183
|
-
const keyInputHTML = `<input type="text" value="${key}" class="form-input form-control w-1/3 px-3 py-2 rounded-md user-config-key" placeholder="Key">`;
|
|
184
|
-
|
|
185
|
-
// Value Input & Eye Icon (if secret)
|
|
186
|
-
let valueSectionHTML;
|
|
187
|
-
if (isSecret) {
|
|
188
|
-
valueSectionHTML = `
|
|
189
|
-
<div class="relative w-2/3">
|
|
190
|
-
<input type="${valueInputType}" value="${value}" class="form-input form-control w-full px-3 py-2 rounded-md user-config-value pr-10" placeholder="Value">
|
|
191
|
-
<button type="button" class="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500 hover:text-gray-700 toggle-visibility-button" style="background: transparent; border: none;">
|
|
192
|
-
<i class='bx bx-show text-lg'></i>
|
|
193
|
-
</button>
|
|
194
|
-
</div>
|
|
195
|
-
`;
|
|
196
|
-
} else {
|
|
197
|
-
valueSectionHTML = `<input type="${valueInputType}" value="${value}" class="form-input form-control w-2/3 px-3 py-2 rounded-md user-config-value" placeholder="Value">`;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Remove Button
|
|
201
|
-
const removeButtonHTML = `
|
|
202
|
-
<button type="button" class="btn btn-danger btn-sm remove-user-config-button flex items-center">
|
|
203
|
-
<i class='bx bx-trash'></i>
|
|
204
|
-
</button>
|
|
205
|
-
`;
|
|
206
|
-
|
|
207
|
-
div.innerHTML = keyInputHTML + valueSectionHTML + removeButtonHTML;
|
|
208
|
-
userConfigurationsContainer.appendChild(div);
|
|
209
|
-
|
|
210
|
-
if (isSecret) {
|
|
211
|
-
const toggleButton = div.querySelector('.toggle-visibility-button');
|
|
212
|
-
const valueInput = div.querySelector('.user-config-value');
|
|
213
|
-
const eyeIcon = toggleButton.querySelector('i');
|
|
214
|
-
|
|
215
|
-
toggleButton.addEventListener('click', (e) => {
|
|
216
|
-
e.preventDefault(); // Prevent form submission if inside a form
|
|
217
|
-
if (valueInput.type === 'password') {
|
|
218
|
-
valueInput.type = 'text';
|
|
219
|
-
eyeIcon.classList.remove('bx-show');
|
|
220
|
-
eyeIcon.classList.add('bx-hide');
|
|
221
|
-
} else {
|
|
222
|
-
valueInput.type = 'password';
|
|
223
|
-
eyeIcon.classList.remove('bx-hide');
|
|
224
|
-
eyeIcon.classList.add('bx-show');
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
div.querySelector('.remove-user-config-button').addEventListener('click', function() {
|
|
230
|
-
this.closest('.user-config-row').remove();
|
|
231
|
-
if (!userConfigurationsContainer.querySelector('.user-config-row')) { // Add placeholder if no rows exist after removal
|
|
232
|
-
const p = document.createElement('p');
|
|
233
|
-
p.className = 'text-gray-500 user-config-placeholder';
|
|
234
|
-
p.textContent = 'No user configurations defined. Click "Add User Configuration" to add one.';
|
|
235
|
-
userConfigurationsContainer.appendChild(p);
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
async function saveSettings(event) {
|
|
242
|
-
event.preventDefault();
|
|
243
|
-
setupButton.disabled = true;
|
|
244
|
-
setupButton.innerHTML = `<i class='bx bx-loader-alt bx-spin mr-2'></i>Saving...`;
|
|
245
|
-
|
|
246
|
-
const pythonEnvs = {};
|
|
247
|
-
document.querySelectorAll('.python-env-row').forEach(row => {
|
|
248
|
-
const serverInput = row.querySelector('.python-env-server');
|
|
249
|
-
const pathInput = row.querySelector('.python-env-path');
|
|
250
|
-
if (serverInput && pathInput && serverInput.value.trim()) {
|
|
251
|
-
pythonEnvs[serverInput.value.trim()] = pathInput.value.trim();
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
const userConfigs = {};
|
|
256
|
-
document.querySelectorAll('.user-config-row').forEach(row => {
|
|
257
|
-
const keyInput = row.querySelector('.user-config-key');
|
|
258
|
-
const valueInput = row.querySelector('.user-config-value');
|
|
259
|
-
if (keyInput && valueInput && keyInput.value.trim()) {
|
|
260
|
-
userConfigs[keyInput.value.trim()] = valueInput.value.trim();
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
const settingsData = {
|
|
265
|
-
pythonEnvs: pythonEnvs,
|
|
266
|
-
// Include pythonEnv for backward compatibility (use system value if available)
|
|
267
|
-
pythonEnv: pythonEnvs['system'] || null,
|
|
268
|
-
nodePath: nodePathInput.value.trim() || null,
|
|
269
|
-
browserPath: browserPathInput.value.trim() || null,
|
|
270
|
-
userConfigurations: userConfigs,
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
try {
|
|
274
|
-
const response = await fetch('/api/settings', {
|
|
275
|
-
method: 'POST',
|
|
276
|
-
headers: {
|
|
277
|
-
'Content-Type': 'application/json',
|
|
278
|
-
},
|
|
279
|
-
body: JSON.stringify(settingsData),
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
const apiResponse = await response.json();
|
|
283
|
-
|
|
284
|
-
if (response.ok && apiResponse.success) {
|
|
285
|
-
showToast('Settings saved successfully!', 'success');
|
|
286
|
-
// Optionally, reload settings to reflect any backend-applied defaults
|
|
287
|
-
await loadSettings();
|
|
288
|
-
} else {
|
|
289
|
-
throw new Error(apiResponse.error || `HTTP error! status: ${response.status}`);
|
|
290
|
-
}
|
|
291
|
-
} catch (error) {
|
|
292
|
-
console.error('Error saving settings:', error);
|
|
293
|
-
showToast(`Error saving settings: ${error.message}`, 'danger');
|
|
294
|
-
} finally {
|
|
295
|
-
setupButton.disabled = false;
|
|
296
|
-
setupButton.innerHTML = `<i class='bx bx-save mr-2'></i>Save Settings`;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
cancelButton.addEventListener('click', () => {
|
|
301
|
-
// Navigate back to index.html, preserving flight parameters
|
|
302
|
-
window.location.href = buildUrlWithFlights('index.html');
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
addPythonEnvButton.addEventListener('click', () => renderPythonEnvInput());
|
|
306
|
-
addUserConfigButton.addEventListener('click', () => renderUserConfigInput());
|
|
307
|
-
|
|
308
|
-
settingsForm.addEventListener('submit', saveSettings);
|
|
309
|
-
|
|
310
|
-
// Initial load
|
|
311
|
-
loadSettings();
|
|
312
|
-
|
|
313
|
-
// (Collapse/expand functionality removed as requested)
|
|
314
|
-
});
|
|
1
|
+
import{buildUrlWithFlights}from"./flights/flights.js";function showToast(e,t="success"){const n=document.querySelector(".toast-container");if(!n)return;const o=`toast-${Date.now()}`,s=`\n <div id="${o}" class="toast align-items-center text-white bg-${"success"===t?"success":"danger"} border-0" role="alert" aria-live="assertive" aria-atomic="true">\n <div class="d-flex">\n <div class="toast-body">\n ${e}\n </div>\n <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>\n </div>\n </div>\n `;n.insertAdjacentHTML("beforeend",s);const r=document.getElementById(o);new bootstrap.Toast(r,{delay:3e3}).show(),r.addEventListener("hidden.bs.toast",(()=>{r.remove()}))}document.addEventListener("DOMContentLoaded",(()=>{const e=document.getElementById("settingsForm"),t=document.getElementById("nodePath"),n=document.getElementById("browserPath"),o=document.getElementById("systemEnvironments"),s=document.getElementById("pythonEnvsContainer"),r=document.getElementById("addPythonEnvButton"),a=document.getElementById("pythonEnvsLoadingMsg"),i=document.getElementById("userConfigurationsContainer"),l=document.getElementById("addUserConfigButton"),c=document.getElementById("userConfigLoadingMsg"),d=document.getElementById("cancelButton"),u=document.getElementById("setupButton");async function m(){try{const e=await fetch("/api/settings");if(!e.ok){const t=await e.json();throw new Error(t.error||`HTTP error! status: ${e.status}`)}const r=await e.json();if(!r.success||!r.data)throw new Error(r.error||"Failed to load settings.");{const e=r.data;if(a&&a.remove(),s.innerHTML="",e.pythonEnvs&&Object.keys(e.pythonEnvs).length>0)Object.entries(e.pythonEnvs).forEach((([e,t])=>{y(e,t)}));else if(e.pythonEnv)y("system",e.pythonEnv);else if(!s.querySelector(".python-env-row")){const e=document.createElement("p");e.className="text-gray-500 python-env-placeholder",e.textContent='No Python environments defined. Click "Add Python Environment" to add one.',s.appendChild(e)}if(t.value=e.nodePath||"",n.value=e.browserPath||"",o.innerHTML="",e.systemEnvironments&&Object.keys(e.systemEnvironments).length>0){const t=document.createElement("div");t.className="flex flex-col",Object.entries(e.systemEnvironments).forEach((([e,n],o)=>{const s=document.createElement("div");s.className="flex flex-col sm:flex-row sm:items-center bg-white rounded-md border border-gray-200 px-4 py-2 shadow-sm hover:shadow transition group",s.innerHTML=`\n <div class="flex items-center min-w-0 w-full sm:w-1/4 sm:mb-0">\n <i class='bx bx-cog text-blue-400 mr-2'></i>\n <span class="font-mono font-semibold text-gray-800 text-base truncate" title="${e}">${e}</span>\n </div>\n <div class="flex-1 min-w-0 break-all text-gray-700 text-sm pl-7 sm:pl-4">\n ${n}\n </div>\n `,t.appendChild(s)})),o.classList.remove("shadow"),o.classList.add("rounded-lg","bg-white","border","border-gray-200","border-1"),o.classList.remove("border-2","border-4","border-8"),o.classList.add("p-0"),o.style.padding="0.5rem 0.5rem 0.5rem 0.5rem",o.appendChild(t)}else o.innerHTML='<p class="text-gray-500">No system environment variables loaded or available.</p>';if(c&&c.remove(),i.innerHTML="",e.userConfigurations&&Object.keys(e.userConfigurations).length>0)Object.entries(e.userConfigurations).forEach((([e,t])=>{p(e,t)}));else if(!i.querySelector(".user-config-row")){const e=document.createElement("p");e.className="text-gray-500 user-config-placeholder",e.textContent='No user configurations defined. Click "Add User Configuration" to add one.',i.appendChild(e)}}}catch(e){showToast(`Error loading settings: ${e.message}`,"danger"),o.innerHTML=`<p class="text-red-500">Error loading environment variables: ${e.message}</p>`,c&&(c.textContent=`Error loading user configurations: ${e.message}`)}}function y(e="system",t=""){const n=s.querySelector(".python-env-placeholder");n&&n.remove(),Date.now(),Math.random().toString(36).substr(2,5);const o=document.createElement("div");o.className="python-env-row flex items-center gap-3 mb-2",o.innerHTML=`\n <input type="text" value="${e}" class="form-input form-control w-1/3 px-3 py-2 rounded-md python-env-server" placeholder="Server Name (e.g., system)">\n <input type="text" value="${t}" class="form-input form-control w-2/3 px-3 py-2 rounded-md python-env-path" placeholder="e.g., /usr/bin/python3 or C:/Python39/python.exe">\n <button type="button" class="btn btn-danger btn-sm remove-python-env-button flex items-center">\n <i class='bx bx-trash'></i>\n </button>\n `,s.appendChild(o),o.querySelector(".remove-python-env-button").addEventListener("click",(function(){if(this.closest(".python-env-row").remove(),!s.querySelector(".python-env-row")){const e=document.createElement("p");e.className="text-gray-500 python-env-placeholder",e.textContent='No Python environments defined. Click "Add Python Environment" to add one.',s.appendChild(e)}}))}function p(e="",t=""){const n=i.querySelector(".user-config-placeholder");n&&n.remove(),Date.now(),Math.random().toString(36).substr(2,5);const o=document.createElement("div");o.className="user-config-row flex items-center gap-3 mb-2";const s=e.toLowerCase().includes("key"),r=s?"password":"text",a=`<input type="text" value="${e}" class="form-input form-control w-1/3 px-3 py-2 rounded-md user-config-key" placeholder="Key">`;let l;if(l=s?`\n <div class="relative w-2/3">\n <input type="${r}" value="${t}" class="form-input form-control w-full px-3 py-2 rounded-md user-config-value pr-10" placeholder="Value">\n <button type="button" class="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500 hover:text-gray-700 toggle-visibility-button" style="background: transparent; border: none;">\n <i class='bx bx-show text-lg'></i>\n </button>\n </div>\n `:`<input type="${r}" value="${t}" class="form-input form-control w-2/3 px-3 py-2 rounded-md user-config-value" placeholder="Value">`,o.innerHTML=a+l+'\n <button type="button" class="btn btn-danger btn-sm remove-user-config-button flex items-center">\n <i class=\'bx bx-trash\'></i>\n </button>\n ',i.appendChild(o),s){const e=o.querySelector(".toggle-visibility-button"),t=o.querySelector(".user-config-value"),n=e.querySelector("i");e.addEventListener("click",(e=>{e.preventDefault(),"password"===t.type?(t.type="text",n.classList.remove("bx-show"),n.classList.add("bx-hide")):(t.type="password",n.classList.remove("bx-hide"),n.classList.add("bx-show"))}))}o.querySelector(".remove-user-config-button").addEventListener("click",(function(){if(this.closest(".user-config-row").remove(),!i.querySelector(".user-config-row")){const e=document.createElement("p");e.className="text-gray-500 user-config-placeholder",e.textContent='No user configurations defined. Click "Add User Configuration" to add one.',i.appendChild(e)}}))}d.addEventListener("click",(()=>{window.location.href=buildUrlWithFlights("index.html")})),r.addEventListener("click",(()=>y())),l.addEventListener("click",(()=>p())),e.addEventListener("submit",(async function(e){e.preventDefault(),u.disabled=!0,u.innerHTML="<i class='bx bx-loader-alt bx-spin mr-2'></i>Saving...";const o={};document.querySelectorAll(".python-env-row").forEach((e=>{const t=e.querySelector(".python-env-server"),n=e.querySelector(".python-env-path");t&&n&&t.value.trim()&&(o[t.value.trim()]=n.value.trim())}));const s={};document.querySelectorAll(".user-config-row").forEach((e=>{const t=e.querySelector(".user-config-key"),n=e.querySelector(".user-config-value");t&&n&&t.value.trim()&&(s[t.value.trim()]=n.value.trim())}));const r={pythonEnvs:o,pythonEnv:o.system||null,nodePath:t.value.trim()||null,browserPath:n.value.trim()||null,userConfigurations:s};try{const e=await fetch("/api/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)}),t=await e.json();if(!e.ok||!t.success)throw new Error(t.error||`HTTP error! status: ${e.status}`);showToast("Settings saved successfully!","success"),await m()}catch(e){showToast(`Error saving settings: ${e.message}`,"danger")}finally{u.disabled=!1,u.innerHTML="<i class='bx bx-save mr-2'></i>Save Settings"}})),m()}));
|
|
2
|
+
//# sourceMappingURL=settings.js.map
|