@mcp-shark/mcp-shark 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/LICENSE +85 -0
  2. package/README.md +724 -0
  3. package/bin/mcp-shark.js +93 -0
  4. package/mcp-server/.editorconfig +15 -0
  5. package/mcp-server/.prettierignore +11 -0
  6. package/mcp-server/.prettierrc +12 -0
  7. package/mcp-server/README.md +280 -0
  8. package/mcp-server/commitlint.config.cjs +42 -0
  9. package/mcp-server/eslint.config.js +131 -0
  10. package/mcp-server/lib/auditor/audit.js +228 -0
  11. package/mcp-server/lib/common/error.js +15 -0
  12. package/mcp-server/lib/server/external/all.js +32 -0
  13. package/mcp-server/lib/server/external/config.js +59 -0
  14. package/mcp-server/lib/server/external/kv.js +102 -0
  15. package/mcp-server/lib/server/external/single/client.js +35 -0
  16. package/mcp-server/lib/server/external/single/request.js +49 -0
  17. package/mcp-server/lib/server/external/single/run.js +75 -0
  18. package/mcp-server/lib/server/external/single/transport.js +57 -0
  19. package/mcp-server/lib/server/internal/handlers/common.js +20 -0
  20. package/mcp-server/lib/server/internal/handlers/error.js +7 -0
  21. package/mcp-server/lib/server/internal/handlers/prompts-get.js +22 -0
  22. package/mcp-server/lib/server/internal/handlers/prompts-list.js +12 -0
  23. package/mcp-server/lib/server/internal/handlers/resources-list.js +12 -0
  24. package/mcp-server/lib/server/internal/handlers/resources-read.js +19 -0
  25. package/mcp-server/lib/server/internal/handlers/tools-call.js +37 -0
  26. package/mcp-server/lib/server/internal/handlers/tools-list.js +14 -0
  27. package/mcp-server/lib/server/internal/run.js +49 -0
  28. package/mcp-server/lib/server/internal/server.js +63 -0
  29. package/mcp-server/lib/server/internal/session.js +39 -0
  30. package/mcp-server/mcp-shark.js +72 -0
  31. package/mcp-server/package-lock.json +4784 -0
  32. package/mcp-server/package.json +30 -0
  33. package/package.json +103 -0
  34. package/ui/README.md +212 -0
  35. package/ui/index.html +16 -0
  36. package/ui/package-lock.json +3574 -0
  37. package/ui/package.json +12 -0
  38. package/ui/paths.js +282 -0
  39. package/ui/public/og-image.png +0 -0
  40. package/ui/server/routes/backups.js +251 -0
  41. package/ui/server/routes/composite.js +244 -0
  42. package/ui/server/routes/config.js +175 -0
  43. package/ui/server/routes/conversations.js +25 -0
  44. package/ui/server/routes/help.js +43 -0
  45. package/ui/server/routes/logs.js +32 -0
  46. package/ui/server/routes/playground.js +152 -0
  47. package/ui/server/routes/requests.js +235 -0
  48. package/ui/server/routes/sessions.js +27 -0
  49. package/ui/server/routes/smartscan/discover.js +117 -0
  50. package/ui/server/routes/smartscan/scans/clearCache.js +22 -0
  51. package/ui/server/routes/smartscan/scans/createBatchScans.js +123 -0
  52. package/ui/server/routes/smartscan/scans/createScan.js +42 -0
  53. package/ui/server/routes/smartscan/scans/getCachedResults.js +51 -0
  54. package/ui/server/routes/smartscan/scans/getScan.js +41 -0
  55. package/ui/server/routes/smartscan/scans/listScans.js +24 -0
  56. package/ui/server/routes/smartscan/scans.js +13 -0
  57. package/ui/server/routes/smartscan/token.js +56 -0
  58. package/ui/server/routes/smartscan/transport.js +53 -0
  59. package/ui/server/routes/smartscan.js +24 -0
  60. package/ui/server/routes/statistics.js +83 -0
  61. package/ui/server/utils/config-update.js +212 -0
  62. package/ui/server/utils/config.js +98 -0
  63. package/ui/server/utils/paths.js +23 -0
  64. package/ui/server/utils/port.js +28 -0
  65. package/ui/server/utils/process.js +80 -0
  66. package/ui/server/utils/scan-cache/all-results.js +180 -0
  67. package/ui/server/utils/scan-cache/directory.js +35 -0
  68. package/ui/server/utils/scan-cache/file-operations.js +104 -0
  69. package/ui/server/utils/scan-cache/hash.js +47 -0
  70. package/ui/server/utils/scan-cache/server-operations.js +80 -0
  71. package/ui/server/utils/scan-cache.js +12 -0
  72. package/ui/server/utils/serialization.js +13 -0
  73. package/ui/server/utils/smartscan-token.js +42 -0
  74. package/ui/server.js +199 -0
  75. package/ui/src/App.jsx +153 -0
  76. package/ui/src/CompositeLogs.jsx +164 -0
  77. package/ui/src/CompositeSetup.jsx +285 -0
  78. package/ui/src/HelpGuide/HelpGuideContent.jsx +118 -0
  79. package/ui/src/HelpGuide/HelpGuideFooter.jsx +58 -0
  80. package/ui/src/HelpGuide/HelpGuideHeader.jsx +56 -0
  81. package/ui/src/HelpGuide.jsx +65 -0
  82. package/ui/src/IntroTour.jsx +140 -0
  83. package/ui/src/LogDetail.jsx +122 -0
  84. package/ui/src/LogTable.jsx +242 -0
  85. package/ui/src/PacketDetail.jsx +190 -0
  86. package/ui/src/PacketFilters.jsx +222 -0
  87. package/ui/src/PacketList.jsx +183 -0
  88. package/ui/src/SmartScan.jsx +178 -0
  89. package/ui/src/TabNavigation.jsx +143 -0
  90. package/ui/src/components/App/HelpButton.jsx +64 -0
  91. package/ui/src/components/App/TrafficTab.jsx +69 -0
  92. package/ui/src/components/App/useAppState.js +163 -0
  93. package/ui/src/components/BackupList.jsx +192 -0
  94. package/ui/src/components/CollapsibleSection.jsx +82 -0
  95. package/ui/src/components/ConfigFileSection.jsx +84 -0
  96. package/ui/src/components/ConfigViewerModal.jsx +141 -0
  97. package/ui/src/components/ConfirmationModal.jsx +129 -0
  98. package/ui/src/components/DetailsTab/BodySection.jsx +27 -0
  99. package/ui/src/components/DetailsTab/CollapsibleRequestResponse.jsx +70 -0
  100. package/ui/src/components/DetailsTab/HeadersSection.jsx +25 -0
  101. package/ui/src/components/DetailsTab/InfoSection.jsx +28 -0
  102. package/ui/src/components/DetailsTab/NetworkInfoSection.jsx +63 -0
  103. package/ui/src/components/DetailsTab/ProtocolInfoSection.jsx +75 -0
  104. package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +46 -0
  105. package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +66 -0
  106. package/ui/src/components/DetailsTab.jsx +31 -0
  107. package/ui/src/components/DetectedPathsList.jsx +171 -0
  108. package/ui/src/components/FileInput.jsx +144 -0
  109. package/ui/src/components/GroupHeader.jsx +76 -0
  110. package/ui/src/components/GroupedByMcpView.jsx +103 -0
  111. package/ui/src/components/GroupedByServerView.jsx +134 -0
  112. package/ui/src/components/GroupedBySessionView.jsx +127 -0
  113. package/ui/src/components/GroupedViews.jsx +2 -0
  114. package/ui/src/components/HexTab.jsx +188 -0
  115. package/ui/src/components/LogsDisplay.jsx +93 -0
  116. package/ui/src/components/LogsToolbar.jsx +193 -0
  117. package/ui/src/components/McpPlayground/LoadingModal.jsx +113 -0
  118. package/ui/src/components/McpPlayground/PromptsSection/PromptCallPanel.jsx +125 -0
  119. package/ui/src/components/McpPlayground/PromptsSection/PromptItem.jsx +48 -0
  120. package/ui/src/components/McpPlayground/PromptsSection/PromptsList.jsx +45 -0
  121. package/ui/src/components/McpPlayground/PromptsSection.jsx +106 -0
  122. package/ui/src/components/McpPlayground/ResourcesSection/ResourceCallPanel.jsx +89 -0
  123. package/ui/src/components/McpPlayground/ResourcesSection/ResourceItem.jsx +59 -0
  124. package/ui/src/components/McpPlayground/ResourcesSection/ResourcesList.jsx +45 -0
  125. package/ui/src/components/McpPlayground/ResourcesSection.jsx +91 -0
  126. package/ui/src/components/McpPlayground/ToolsSection/ToolCallPanel.jsx +125 -0
  127. package/ui/src/components/McpPlayground/ToolsSection/ToolItem.jsx +48 -0
  128. package/ui/src/components/McpPlayground/ToolsSection/ToolsList.jsx +45 -0
  129. package/ui/src/components/McpPlayground/ToolsSection.jsx +107 -0
  130. package/ui/src/components/McpPlayground/common/EmptyState.jsx +17 -0
  131. package/ui/src/components/McpPlayground/common/ErrorState.jsx +17 -0
  132. package/ui/src/components/McpPlayground/common/LoadingState.jsx +17 -0
  133. package/ui/src/components/McpPlayground/useMcpPlayground.js +280 -0
  134. package/ui/src/components/McpPlayground.jsx +171 -0
  135. package/ui/src/components/MessageDisplay.jsx +28 -0
  136. package/ui/src/components/PacketDetailHeader.jsx +88 -0
  137. package/ui/src/components/PacketFilters/ExportControls.jsx +126 -0
  138. package/ui/src/components/PacketFilters/FilterInput.jsx +59 -0
  139. package/ui/src/components/RawTab.jsx +142 -0
  140. package/ui/src/components/RequestRow/OrphanedResponseRow.jsx +155 -0
  141. package/ui/src/components/RequestRow/RequestRowMain.jsx +240 -0
  142. package/ui/src/components/RequestRow/ResponseRow.jsx +158 -0
  143. package/ui/src/components/RequestRow.jsx +70 -0
  144. package/ui/src/components/ServerControl.jsx +133 -0
  145. package/ui/src/components/ServiceSelector.jsx +209 -0
  146. package/ui/src/components/SetupHeader.jsx +30 -0
  147. package/ui/src/components/SharkLogo.jsx +21 -0
  148. package/ui/src/components/SmartScan/AnalysisResult.jsx +64 -0
  149. package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultItem.jsx +215 -0
  150. package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultsHeader.jsx +94 -0
  151. package/ui/src/components/SmartScan/BatchResultsDisplay.jsx +26 -0
  152. package/ui/src/components/SmartScan/DebugInfoSection.jsx +53 -0
  153. package/ui/src/components/SmartScan/EmptyState.jsx +57 -0
  154. package/ui/src/components/SmartScan/ErrorDisplay.jsx +48 -0
  155. package/ui/src/components/SmartScan/ExpandableSection.jsx +93 -0
  156. package/ui/src/components/SmartScan/FindingsTable.jsx +257 -0
  157. package/ui/src/components/SmartScan/ListViewContent.jsx +75 -0
  158. package/ui/src/components/SmartScan/NotablePatternsSection.jsx +75 -0
  159. package/ui/src/components/SmartScan/OverallSummarySection.jsx +72 -0
  160. package/ui/src/components/SmartScan/RawDataSection.jsx +52 -0
  161. package/ui/src/components/SmartScan/RecommendationsSection.jsx +78 -0
  162. package/ui/src/components/SmartScan/ScanDetailHeader.jsx +92 -0
  163. package/ui/src/components/SmartScan/ScanDetailView.jsx +141 -0
  164. package/ui/src/components/SmartScan/ScanListView/ScanListHeader.jsx +49 -0
  165. package/ui/src/components/SmartScan/ScanListView/ScanListItem.jsx +201 -0
  166. package/ui/src/components/SmartScan/ScanListView.jsx +73 -0
  167. package/ui/src/components/SmartScan/ScanOverviewSection.jsx +123 -0
  168. package/ui/src/components/SmartScan/ScanResultsDisplay.jsx +35 -0
  169. package/ui/src/components/SmartScan/ScanViewContent.jsx +68 -0
  170. package/ui/src/components/SmartScan/ScanningProgress.jsx +47 -0
  171. package/ui/src/components/SmartScan/ServerInfoSection.jsx +43 -0
  172. package/ui/src/components/SmartScan/ServerSelectionRow.jsx +207 -0
  173. package/ui/src/components/SmartScan/SingleResultDisplay.jsx +269 -0
  174. package/ui/src/components/SmartScan/SmartScanControls.jsx +290 -0
  175. package/ui/src/components/SmartScan/SmartScanHeader.jsx +77 -0
  176. package/ui/src/components/SmartScan/ViewModeTabs.jsx +57 -0
  177. package/ui/src/components/SmartScan/hooks/useCacheManagement.js +34 -0
  178. package/ui/src/components/SmartScan/hooks/useMcpDiscovery.js +121 -0
  179. package/ui/src/components/SmartScan/hooks/useScanList.js +193 -0
  180. package/ui/src/components/SmartScan/hooks/useScanOperations.js +87 -0
  181. package/ui/src/components/SmartScan/hooks/useServerStatus.js +26 -0
  182. package/ui/src/components/SmartScan/hooks/useTokenManagement.js +53 -0
  183. package/ui/src/components/SmartScan/scanDataUtils.js +98 -0
  184. package/ui/src/components/SmartScan/useSmartScan.js +72 -0
  185. package/ui/src/components/SmartScan/utils.js +19 -0
  186. package/ui/src/components/SmartScanIcons.jsx +58 -0
  187. package/ui/src/components/TabNavigation/DesktopTabs.jsx +111 -0
  188. package/ui/src/components/TabNavigation/MobileDropdown.jsx +140 -0
  189. package/ui/src/components/TabNavigation.jsx +97 -0
  190. package/ui/src/components/TabNavigationIcons.jsx +40 -0
  191. package/ui/src/components/TableHeader.jsx +164 -0
  192. package/ui/src/components/TourOverlay.jsx +117 -0
  193. package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +117 -0
  194. package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +70 -0
  195. package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +45 -0
  196. package/ui/src/components/TourTooltip/useTooltipPosition.js +108 -0
  197. package/ui/src/components/TourTooltip.jsx +83 -0
  198. package/ui/src/components/ViewModeTabs.jsx +91 -0
  199. package/ui/src/components/WhatThisDoesSection.jsx +61 -0
  200. package/ui/src/config/tourSteps.jsx +141 -0
  201. package/ui/src/hooks/useAnimation.js +92 -0
  202. package/ui/src/hooks/useConfigManagement.js +124 -0
  203. package/ui/src/hooks/useServiceExtraction.js +51 -0
  204. package/ui/src/index.css +42 -0
  205. package/ui/src/main.jsx +10 -0
  206. package/ui/src/theme.js +65 -0
  207. package/ui/src/utils/animations.js +170 -0
  208. package/ui/src/utils/groupingUtils.js +93 -0
  209. package/ui/src/utils/hexUtils.js +24 -0
  210. package/ui/src/utils/mcpGroupingUtils.js +262 -0
  211. package/ui/src/utils/requestUtils.js +297 -0
  212. package/ui/vite.config.js +18 -0
@@ -0,0 +1,141 @@
1
+ import React from 'react';
2
+
3
+ export const tourSteps = [
4
+ {
5
+ target: '[data-tour="tabs"]',
6
+ title: 'Welcome to MCP Shark!',
7
+ content: (
8
+ <div>
9
+ <p style={{ margin: '0 0 12px 0' }}>
10
+ MCP Shark is a powerful tool for monitoring and analyzing Model Context Protocol (MCP)
11
+ communications. Let's get you started!
12
+ </p>
13
+ <p style={{ margin: 0 }}>
14
+ First, you'll need to set up the MCP Shark server. Click on the{' '}
15
+ <strong>MCP Server Setup</strong> tab to begin.
16
+ </p>
17
+ </div>
18
+ ),
19
+ position: 'bottom',
20
+ },
21
+ {
22
+ target: '[data-tour="setup-tab"]',
23
+ title: 'Step 1: Open MCP Server Setup',
24
+ content: (
25
+ <div>
26
+ <p style={{ margin: '0 0 8px 0' }}>
27
+ Click on the <strong>MCP Server Setup</strong> tab to configure and start the MCP Shark
28
+ server.
29
+ </p>
30
+ <p style={{ margin: 0, fontSize: '12px', color: '#858585' }}>
31
+ This is where you'll configure your MCP servers and start monitoring.
32
+ </p>
33
+ </div>
34
+ ),
35
+ position: 'bottom',
36
+ },
37
+ {
38
+ target: '[data-tour="detected-editors"]',
39
+ title: 'Step 2: Select Your Configuration',
40
+ content: (
41
+ <div>
42
+ <p style={{ margin: '0 0 8px 0' }}>
43
+ MCP Shark automatically detects your IDE's MCP configuration files. You have two options:
44
+ </p>
45
+ <ul style={{ margin: '0 0 8px 0', paddingLeft: '20px', fontSize: '13px' }}>
46
+ <li>
47
+ Click on any <strong>detected editor</strong> (like Cursor or Windsurf) to use its
48
+ config
49
+ </li>
50
+ <li>
51
+ Or click <strong>"Select File"</strong> to upload your own config file
52
+ </li>
53
+ </ul>
54
+ <p style={{ margin: 0, fontSize: '12px', color: '#858585' }}>
55
+ When you click a detected editor, the file path will automatically populate in the text
56
+ box.
57
+ </p>
58
+ </div>
59
+ ),
60
+ position: 'bottom',
61
+ },
62
+ {
63
+ target: '[data-tour="select-file"]',
64
+ title: 'Alternative: Upload Your Config',
65
+ content: (
66
+ <div>
67
+ <p style={{ margin: '0 0 8px 0' }}>
68
+ If you prefer, you can click <strong>"Select File"</strong> to upload your MCP
69
+ configuration file directly.
70
+ </p>
71
+ <p style={{ margin: 0, fontSize: '12px', color: '#858585' }}>
72
+ Or manually enter the file path in the text box next to it.
73
+ </p>
74
+ </div>
75
+ ),
76
+ position: 'bottom',
77
+ },
78
+ {
79
+ target: '[data-tour="start-button"]',
80
+ title: 'Step 3: Start MCP Shark',
81
+ content: (
82
+ <div>
83
+ <p style={{ margin: '0 0 8px 0' }}>
84
+ Once you've selected a configuration file (either from detected editors or uploaded),
85
+ click <strong>"Start MCP Shark"</strong> to begin monitoring.
86
+ </p>
87
+ <p style={{ margin: 0, fontSize: '12px', color: '#858585' }}>
88
+ The server will start and begin capturing all MCP traffic between your IDE and servers.
89
+ </p>
90
+ </div>
91
+ ),
92
+ position: 'top',
93
+ },
94
+ {
95
+ target: '[data-tour="traffic-tab"]',
96
+ title: 'View Your Traffic',
97
+ content: (
98
+ <div>
99
+ <p style={{ margin: '0 0 8px 0' }}>
100
+ After starting the server, switch to the <strong>Traffic Capture</strong> tab to see all
101
+ HTTP requests and responses in real-time.
102
+ </p>
103
+ <p style={{ margin: 0, fontSize: '12px', color: '#858585' }}>
104
+ You can view traffic as a flat list, grouped by session, or grouped by server.
105
+ </p>
106
+ </div>
107
+ ),
108
+ position: 'bottom',
109
+ },
110
+ {
111
+ target: '[data-tour="smart-scan-tab"]',
112
+ title: 'Smart Scan - Security Analysis',
113
+ content: (
114
+ <div>
115
+ <p style={{ margin: '0 0 8px 0' }}>
116
+ The <strong>Smart Scan</strong> tab provides AI-powered security analysis for your MCP
117
+ servers. Discover servers, run security scans, and get detailed vulnerability reports.
118
+ </p>
119
+ <p style={{ margin: 0, fontSize: '12px', color: '#858585' }}>
120
+ Scan results are cached automatically, so you won't waste API calls on unchanged servers.
121
+ </p>
122
+ </div>
123
+ ),
124
+ position: 'bottom',
125
+ },
126
+ {
127
+ target: '[data-tour="help-button"]',
128
+ title: 'Need Help?',
129
+ content: (
130
+ <div>
131
+ <p style={{ margin: '0 0 8px 0' }}>
132
+ Click the <strong>Start Tour</strong> button anytime to restart this guide or get help.
133
+ </p>
134
+ <p style={{ margin: 0, fontSize: '12px', color: '#858585' }}>
135
+ You're all set! Start by configuring your MCP server, then watch the traffic flow.
136
+ </p>
137
+ </div>
138
+ ),
139
+ position: 'left',
140
+ },
141
+ ];
@@ -0,0 +1,92 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import anime from 'animejs';
3
+
4
+ /**
5
+ * React hook for animating elements on mount/unmount
6
+ */
7
+ export const useAnimation = (animationFn, deps = []) => {
8
+ const elementRef = useRef(null);
9
+ const animationRef = useRef(null);
10
+
11
+ useEffect(() => {
12
+ if (elementRef.current && animationFn) {
13
+ animationRef.current = animationFn(elementRef.current);
14
+ }
15
+
16
+ return () => {
17
+ if (animationRef.current) {
18
+ anime.remove(elementRef.current);
19
+ }
20
+ };
21
+ }, deps);
22
+
23
+ return elementRef;
24
+ };
25
+
26
+ /**
27
+ * Hook for animating on mount
28
+ */
29
+ export const useMountAnimation = (options = {}) => {
30
+ const elementRef = useRef(null);
31
+
32
+ useEffect(() => {
33
+ if (elementRef.current) {
34
+ anime({
35
+ targets: elementRef.current,
36
+ opacity: [0, 1],
37
+ translateY: [20, 0],
38
+ duration: options.duration || 400,
39
+ easing: options.easing || 'easeOutExpo',
40
+ ...options,
41
+ });
42
+ }
43
+ }, []);
44
+
45
+ return elementRef;
46
+ };
47
+
48
+ /**
49
+ * Hook for animating on unmount
50
+ */
51
+ export const useUnmountAnimation = (shouldUnmount, onComplete, options = {}) => {
52
+ const elementRef = useRef(null);
53
+
54
+ useEffect(() => {
55
+ if (shouldUnmount && elementRef.current) {
56
+ anime({
57
+ targets: elementRef.current,
58
+ opacity: [1, 0],
59
+ translateY: [0, -20],
60
+ duration: options.duration || 300,
61
+ easing: options.easing || 'easeInExpo',
62
+ complete: onComplete,
63
+ ...options,
64
+ });
65
+ }
66
+ }, [shouldUnmount, onComplete]);
67
+
68
+ return elementRef;
69
+ };
70
+
71
+ /**
72
+ * Hook for staggered list animations
73
+ */
74
+ export const useStaggerAnimation = (items, options = {}) => {
75
+ const containerRef = useRef(null);
76
+
77
+ useEffect(() => {
78
+ if (containerRef.current && items.length > 0) {
79
+ anime({
80
+ targets: containerRef.current.children,
81
+ opacity: [0, 1],
82
+ translateY: [20, 0],
83
+ duration: options.duration || 400,
84
+ delay: anime.stagger(options.delay || 50),
85
+ easing: options.easing || 'easeOutExpo',
86
+ ...options,
87
+ });
88
+ }
89
+ }, [items.length]);
90
+
91
+ return containerRef;
92
+ };
@@ -0,0 +1,124 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ export function useConfigManagement() {
4
+ const [detectedPaths, setDetectedPaths] = useState([]);
5
+ const [detecting, setDetecting] = useState(true);
6
+ const [backups, setBackups] = useState([]);
7
+ const [loadingBackups, setLoadingBackups] = useState(false);
8
+ const [viewingConfig, setViewingConfig] = useState(null);
9
+ const [configContent, setConfigContent] = useState(null);
10
+ const [loadingConfig, setLoadingConfig] = useState(false);
11
+ const [viewingBackup, setViewingBackup] = useState(null);
12
+ const [backupContent, setBackupContent] = useState(null);
13
+ const [loadingBackup, setLoadingBackup] = useState(false);
14
+
15
+ const detectConfigPaths = async () => {
16
+ setDetecting(true);
17
+ try {
18
+ const res = await fetch('/api/config/detect');
19
+ const data = await res.json();
20
+ setDetectedPaths(data.detected || []);
21
+ } catch (err) {
22
+ console.error('Failed to detect config paths:', err);
23
+ } finally {
24
+ setDetecting(false);
25
+ }
26
+ };
27
+
28
+ const loadBackups = async () => {
29
+ setLoadingBackups(true);
30
+ try {
31
+ const res = await fetch('/api/config/backups');
32
+ const data = await res.json();
33
+ setBackups(data.backups || []);
34
+ } catch (err) {
35
+ console.error('Failed to load backups:', err);
36
+ } finally {
37
+ setLoadingBackups(false);
38
+ }
39
+ };
40
+
41
+ const handleViewConfig = async (filePath) => {
42
+ setLoadingConfig(true);
43
+ setViewingConfig(filePath);
44
+ try {
45
+ const res = await fetch(`/api/config/read?filePath=${encodeURIComponent(filePath)}`);
46
+ const data = await res.json();
47
+ if (res.ok) {
48
+ setConfigContent(data);
49
+ } else {
50
+ setConfigContent(null);
51
+ }
52
+ } catch (err) {
53
+ setConfigContent(null);
54
+ } finally {
55
+ setLoadingConfig(false);
56
+ }
57
+ };
58
+
59
+ const handleViewBackup = async (backupPath) => {
60
+ setLoadingBackup(true);
61
+ setViewingBackup(backupPath);
62
+ try {
63
+ const res = await fetch(
64
+ `/api/config/backup/view?backupPath=${encodeURIComponent(backupPath)}`
65
+ );
66
+ const data = await res.json();
67
+ if (res.ok) {
68
+ setBackupContent(data);
69
+ } else {
70
+ setBackupContent(null);
71
+ }
72
+ } catch (err) {
73
+ setBackupContent(null);
74
+ } finally {
75
+ setLoadingBackup(false);
76
+ }
77
+ };
78
+
79
+ const handleDeleteBackup = async (backupPath) => {
80
+ try {
81
+ const res = await fetch('/api/config/backup/delete', {
82
+ method: 'POST',
83
+ headers: { 'Content-Type': 'application/json' },
84
+ body: JSON.stringify({ backupPath }),
85
+ });
86
+
87
+ if (res.ok) {
88
+ await loadBackups();
89
+ return true;
90
+ }
91
+ return false;
92
+ } catch (err) {
93
+ console.error('Failed to delete backup:', err);
94
+ return false;
95
+ }
96
+ };
97
+
98
+ useEffect(() => {
99
+ detectConfigPaths();
100
+ loadBackups();
101
+ }, []);
102
+
103
+ return {
104
+ detectedPaths,
105
+ detecting,
106
+ detectConfigPaths,
107
+ backups,
108
+ loadingBackups,
109
+ loadBackups,
110
+ viewingConfig,
111
+ configContent,
112
+ loadingConfig,
113
+ handleViewConfig,
114
+ setViewingConfig,
115
+ setConfigContent,
116
+ viewingBackup,
117
+ backupContent,
118
+ loadingBackup,
119
+ handleViewBackup,
120
+ handleDeleteBackup,
121
+ setViewingBackup,
122
+ setBackupContent,
123
+ };
124
+ }
@@ -0,0 +1,51 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+
3
+ export function useServiceExtraction(fileContent, filePath) {
4
+ const [services, setServices] = useState([]);
5
+ const [selectedServices, setSelectedServices] = useState(new Set());
6
+ const [loadingServices, setLoadingServices] = useState(false);
7
+
8
+ const extractServices = useCallback(async () => {
9
+ if (!fileContent && !filePath) {
10
+ setServices([]);
11
+ setSelectedServices(new Set());
12
+ return;
13
+ }
14
+
15
+ setLoadingServices(true);
16
+ try {
17
+ const payload = fileContent ? { fileContent } : { filePath };
18
+ const res = await fetch('/api/config/services', {
19
+ method: 'POST',
20
+ headers: { 'Content-Type': 'application/json' },
21
+ body: JSON.stringify(payload),
22
+ });
23
+
24
+ const data = await res.json();
25
+ if (res.ok && data.services) {
26
+ setServices(data.services);
27
+ setSelectedServices(new Set(data.services.map((s) => s.name)));
28
+ } else {
29
+ setServices([]);
30
+ setSelectedServices(new Set());
31
+ }
32
+ } catch (err) {
33
+ console.error('Failed to extract services:', err);
34
+ setServices([]);
35
+ setSelectedServices(new Set());
36
+ } finally {
37
+ setLoadingServices(false);
38
+ }
39
+ }, [fileContent, filePath]);
40
+
41
+ useEffect(() => {
42
+ if (fileContent || filePath) {
43
+ extractServices();
44
+ } else {
45
+ setServices([]);
46
+ setSelectedServices(new Set());
47
+ }
48
+ }, [fileContent, filePath, extractServices]);
49
+
50
+ return { services, selectedServices, setSelectedServices, loadingServices };
51
+ }
@@ -0,0 +1,42 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Roboto+Mono:wght@400;500&display=swap');
2
+
3
+ * {
4
+ margin: 0;
5
+ padding: 0;
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ body {
10
+ font-family:
11
+ 'Roboto',
12
+ -apple-system,
13
+ BlinkMacSystemFont,
14
+ 'Segoe UI',
15
+ sans-serif;
16
+ background: #ffffff;
17
+ color: #202124;
18
+ overflow: hidden;
19
+ -webkit-font-smoothing: antialiased;
20
+ -moz-osx-font-smoothing: grayscale;
21
+ }
22
+
23
+ code,
24
+ pre,
25
+ .monospace {
26
+ font-family: 'Roboto Mono', 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
27
+ }
28
+
29
+ #root {
30
+ height: 100vh;
31
+ display: flex;
32
+ flex-direction: column;
33
+ }
34
+
35
+ @keyframes spin {
36
+ from {
37
+ transform: rotate(0deg);
38
+ }
39
+ to {
40
+ transform: rotate(360deg);
41
+ }
42
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import App from './App';
4
+ import './index.css';
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>
10
+ );
@@ -0,0 +1,65 @@
1
+ // Gmail-inspired clean and minimal color theme
2
+ export const colors = {
3
+ // Backgrounds - Gmail style
4
+ bgPrimary: '#ffffff', // Pure white (Gmail main background)
5
+ bgSecondary: '#f8f9fa', // Very light gray (Gmail sidebar/secondary areas)
6
+ bgTertiary: '#f1f3f4', // Light gray (hover states)
7
+ bgCard: '#ffffff', // Pure white for cards
8
+ bgHover: '#f1f3f4', // Gmail hover color
9
+ bgSelected: '#e8f0fe', // Gmail selected blue tint
10
+ bgUnpaired: '#fef7e0', // Very light orange/yellow for unpaired requests/responses
11
+
12
+ // Text - Gmail style
13
+ textPrimary: '#202124', // Gmail primary text (almost black)
14
+ textSecondary: '#5f6368', // Gmail secondary text (medium gray)
15
+ textTertiary: '#80868b', // Gmail tertiary text (light gray)
16
+ textInverse: '#ffffff', // White text
17
+
18
+ // Borders - Gmail style
19
+ borderLight: '#dadce0', // Gmail light border
20
+ borderMedium: '#bdc1c6', // Gmail medium border
21
+ borderDark: '#9aa0a6', // Gmail dark border
22
+
23
+ // Accents - Gmail/Google Material colors
24
+ accentBlue: '#1a73e8', // Gmail primary blue
25
+ accentBlueHover: '#1557b0', // Darker blue on hover
26
+ accentGreen: '#34a853', // Google green
27
+ accentOrange: '#ea8600', // Google orange
28
+ accentPink: '#d44638', // Gmail red (Jasper)
29
+ accentPurple: '#9334e6', // Google purple
30
+
31
+ // Status colors - Gmail style
32
+ success: '#34a853', // Google green
33
+ warning: '#fbbc04', // Google yellow
34
+ error: '#ea4335', // Google red
35
+ info: '#1a73e8', // Gmail blue
36
+
37
+ // Interactive - Gmail style (lighter, less sharp)
38
+ buttonPrimary: '#4285f4', // Lighter Google blue
39
+ buttonPrimaryHover: '#3367d6',
40
+ buttonSecondary: '#f1f3f4', // Light gray
41
+ buttonSecondaryHover: '#e8eaed',
42
+ buttonDanger: '#ea4335', // Google red (lighter)
43
+ buttonDangerHover: '#d33b2c',
44
+
45
+ // Shadows - Gmail style (subtle)
46
+ shadowSm: 'rgba(60, 64, 67, 0.08)', // Gmail subtle shadow
47
+ shadowMd: 'rgba(60, 64, 67, 0.12)', // Gmail medium shadow
48
+ shadowLg: 'rgba(60, 64, 67, 0.16)', // Gmail larger shadow
49
+ };
50
+
51
+ // Helper function to add opacity to hex colors
52
+ export function withOpacity(color, opacity) {
53
+ // Remove # if present
54
+ const hex = color.replace('#', '');
55
+ // Convert to RGB
56
+ const r = parseInt(hex.substring(0, 2), 16);
57
+ const g = parseInt(hex.substring(2, 4), 16);
58
+ const b = parseInt(hex.substring(4, 6), 16);
59
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
60
+ }
61
+
62
+ export const fonts = {
63
+ body: "'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
64
+ mono: "'Roboto Mono', 'JetBrains Mono', 'Fira Code', 'Consolas', monospace",
65
+ };
@@ -0,0 +1,170 @@
1
+ import anime from 'animejs';
2
+
3
+ /**
4
+ * Animation utilities for modernizing the UI with anime.js
5
+ */
6
+
7
+ /**
8
+ * Stagger animation for list items
9
+ */
10
+ export const staggerIn = (selector, options = {}) => {
11
+ return anime({
12
+ targets: selector,
13
+ opacity: [0, 1],
14
+ translateY: [20, 0],
15
+ duration: options.duration || 400,
16
+ delay: anime.stagger(options.delay || 50),
17
+ easing: options.easing || 'easeOutExpo',
18
+ ...options,
19
+ });
20
+ };
21
+
22
+ /**
23
+ * Fade in animation
24
+ */
25
+ export const fadeIn = (selector, options = {}) => {
26
+ return anime({
27
+ targets: selector,
28
+ opacity: [0, 1],
29
+ duration: options.duration || 300,
30
+ easing: options.easing || 'easeOutQuad',
31
+ ...options,
32
+ });
33
+ };
34
+
35
+ /**
36
+ * Fade out animation
37
+ */
38
+ export const fadeOut = (selector, options = {}) => {
39
+ return anime({
40
+ targets: selector,
41
+ opacity: [1, 0],
42
+ duration: options.duration || 300,
43
+ easing: options.easing || 'easeInQuad',
44
+ ...options,
45
+ });
46
+ };
47
+
48
+ /**
49
+ * Slide in from right
50
+ */
51
+ export const slideInRight = (selector, options = {}) => {
52
+ return anime({
53
+ targets: selector,
54
+ translateX: [options.width || 600, 0],
55
+ opacity: [0, 1],
56
+ duration: options.duration || 400,
57
+ easing: options.easing || 'easeOutExpo',
58
+ ...options,
59
+ });
60
+ };
61
+
62
+ /**
63
+ * Slide in from left
64
+ */
65
+ export const slideInLeft = (selector, options = {}) => {
66
+ return anime({
67
+ targets: selector,
68
+ translateX: [-(options.width || 600), 0],
69
+ opacity: [0, 1],
70
+ duration: options.duration || 400,
71
+ easing: options.easing || 'easeOutExpo',
72
+ ...options,
73
+ });
74
+ };
75
+
76
+ /**
77
+ * Slide out to right
78
+ */
79
+ export const slideOutRight = (selector, options = {}) => {
80
+ return anime({
81
+ targets: selector,
82
+ translateX: [0, options.width || 600],
83
+ opacity: [1, 0],
84
+ duration: options.duration || 300,
85
+ easing: options.easing || 'easeInExpo',
86
+ ...options,
87
+ });
88
+ };
89
+
90
+ /**
91
+ * Scale in animation
92
+ */
93
+ export const scaleIn = (selector, options = {}) => {
94
+ return anime({
95
+ targets: selector,
96
+ scale: [0.9, 1],
97
+ opacity: [0, 1],
98
+ duration: options.duration || 300,
99
+ easing: options.easing || 'easeOutBack',
100
+ ...options,
101
+ });
102
+ };
103
+
104
+ /**
105
+ * Scale out animation
106
+ */
107
+ export const scaleOut = (selector, options = {}) => {
108
+ return anime({
109
+ targets: selector,
110
+ scale: [1, 0.9],
111
+ opacity: [1, 0],
112
+ duration: options.duration || 200,
113
+ easing: options.easing || 'easeInBack',
114
+ ...options,
115
+ });
116
+ };
117
+
118
+ /**
119
+ * Tab indicator animation
120
+ */
121
+ export const animateTabIndicator = (selector, options = {}) => {
122
+ return anime({
123
+ targets: selector,
124
+ translateX: options.translateX || 0,
125
+ width: options.width || 'auto',
126
+ duration: options.duration || 300,
127
+ easing: options.easing || 'easeOutExpo',
128
+ ...options,
129
+ });
130
+ };
131
+
132
+ /**
133
+ * Hover scale animation
134
+ */
135
+ export const hoverScale = (selector, scale = 1.05, options = {}) => {
136
+ return anime({
137
+ targets: selector,
138
+ scale: scale,
139
+ duration: options.duration || 200,
140
+ easing: options.easing || 'easeOutQuad',
141
+ ...options,
142
+ });
143
+ };
144
+
145
+ /**
146
+ * Pulse animation
147
+ */
148
+ export const pulse = (selector, options = {}) => {
149
+ return anime({
150
+ targets: selector,
151
+ scale: [1, 1.1, 1],
152
+ duration: options.duration || 600,
153
+ easing: options.easing || 'easeInOutQuad',
154
+ loop: options.loop || false,
155
+ ...options,
156
+ });
157
+ };
158
+
159
+ /**
160
+ * Shake animation
161
+ */
162
+ export const shake = (selector, options = {}) => {
163
+ return anime({
164
+ targets: selector,
165
+ translateX: [0, -10, 10, -10, 10, 0],
166
+ duration: options.duration || 500,
167
+ easing: options.easing || 'easeInOutQuad',
168
+ ...options,
169
+ });
170
+ };