appclean 1.9.0 → 2.0.2

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/.github/workflows/npm-publish.yml +61 -0
  2. package/DEVELOPMENT.md +84 -0
  3. package/GUI_IMPLEMENTATION_STATUS.md +143 -0
  4. package/MD_Files/INDEX.md +51 -0
  5. package/PHASE2_COMPLETION.md +281 -0
  6. package/PHASE3_COMPLETION.md +364 -0
  7. package/README.md +411 -91
  8. package/RELEASE_GUIDE.md +236 -0
  9. package/assets/logo.svg +34 -0
  10. package/dist/core/appUpdateChecker.js +12 -16
  11. package/dist/core/appUpdateChecker.js.map +1 -1
  12. package/dist/core/detector.js +14 -18
  13. package/dist/core/detector.js.map +1 -1
  14. package/dist/core/duplicateFileFinder.js +12 -19
  15. package/dist/core/duplicateFileFinder.js.map +1 -1
  16. package/dist/core/orphanedDependencyDetector.js +19 -26
  17. package/dist/core/orphanedDependencyDetector.js.map +1 -1
  18. package/dist/core/performanceOptimizer.js +6 -10
  19. package/dist/core/performanceOptimizer.js.map +1 -1
  20. package/dist/core/permissionHandler.js +21 -25
  21. package/dist/core/permissionHandler.js.map +1 -1
  22. package/dist/core/pluginSystem.js +9 -13
  23. package/dist/core/pluginSystem.js.map +1 -1
  24. package/dist/core/removalRecorder.js +12 -19
  25. package/dist/core/removalRecorder.js.map +1 -1
  26. package/dist/core/remover.js +59 -66
  27. package/dist/core/remover.js.map +1 -1
  28. package/dist/core/reportGenerator.d.ts +1 -1
  29. package/dist/core/reportGenerator.d.ts.map +1 -1
  30. package/dist/core/reportGenerator.js +27 -34
  31. package/dist/core/reportGenerator.js.map +1 -1
  32. package/dist/core/scheduledCleanup.js +23 -30
  33. package/dist/core/scheduledCleanup.js.map +1 -1
  34. package/dist/core/serviceFileDetector.js +24 -31
  35. package/dist/core/serviceFileDetector.js.map +1 -1
  36. package/dist/core/verificationModule.js +10 -14
  37. package/dist/core/verificationModule.js.map +1 -1
  38. package/dist/index.js +118 -156
  39. package/dist/index.js.map +1 -1
  40. package/dist/managers/brewManager.js +30 -37
  41. package/dist/managers/brewManager.js.map +1 -1
  42. package/dist/managers/customManager.js +23 -30
  43. package/dist/managers/customManager.js.map +1 -1
  44. package/dist/managers/linuxManager.js +29 -36
  45. package/dist/managers/linuxManager.js.map +1 -1
  46. package/dist/managers/npmManager.js +27 -34
  47. package/dist/managers/npmManager.js.map +1 -1
  48. package/dist/types/index.js +1 -2
  49. package/dist/ui/client/api/client.d.ts +24 -0
  50. package/dist/ui/client/api/client.d.ts.map +1 -0
  51. package/dist/ui/client/api/client.js +100 -0
  52. package/dist/ui/client/api/client.js.map +1 -0
  53. package/dist/ui/client/app.d.ts +7 -0
  54. package/dist/ui/client/app.d.ts.map +1 -0
  55. package/dist/ui/client/app.js +75 -0
  56. package/dist/ui/client/app.js.map +1 -0
  57. package/dist/ui/client/index.html +107 -0
  58. package/dist/ui/client/pages/appDetails.d.ts +8 -0
  59. package/dist/ui/client/pages/appDetails.d.ts.map +1 -0
  60. package/dist/ui/client/pages/appDetails.js +287 -0
  61. package/dist/ui/client/pages/appDetails.js.map +1 -0
  62. package/dist/ui/client/pages/appSearch.d.ts +2 -0
  63. package/dist/ui/client/pages/appSearch.d.ts.map +1 -0
  64. package/dist/ui/client/pages/appSearch.js +221 -0
  65. package/dist/ui/client/pages/appSearch.js.map +1 -0
  66. package/dist/ui/client/pages/dashboard.d.ts +2 -0
  67. package/dist/ui/client/pages/dashboard.d.ts.map +1 -0
  68. package/dist/ui/client/pages/dashboard.js +175 -0
  69. package/dist/ui/client/pages/dashboard.js.map +1 -0
  70. package/dist/ui/client/pages/settings.d.ts +7 -0
  71. package/dist/ui/client/pages/settings.d.ts.map +1 -0
  72. package/dist/ui/client/pages/settings.js +279 -0
  73. package/dist/ui/client/pages/settings.js.map +1 -0
  74. package/dist/ui/client/state/appStore.d.ts +38 -0
  75. package/dist/ui/client/state/appStore.d.ts.map +1 -0
  76. package/dist/ui/client/state/appStore.js +130 -0
  77. package/dist/ui/client/state/appStore.js.map +1 -0
  78. package/dist/ui/client/state/dashboardStore.d.ts +31 -0
  79. package/dist/ui/client/state/dashboardStore.d.ts.map +1 -0
  80. package/dist/ui/client/state/dashboardStore.js +76 -0
  81. package/dist/ui/client/state/dashboardStore.js.map +1 -0
  82. package/dist/ui/client/state/uiStore.d.ts +43 -0
  83. package/dist/ui/client/state/uiStore.d.ts.map +1 -0
  84. package/dist/ui/client/state/uiStore.js +109 -0
  85. package/dist/ui/client/state/uiStore.js.map +1 -0
  86. package/dist/ui/client/styles/animations.css +349 -0
  87. package/dist/ui/client/styles/base.css +214 -0
  88. package/dist/ui/client/styles/components.css +400 -0
  89. package/dist/ui/client/styles/layout.css +224 -0
  90. package/dist/ui/client/styles/variables.css +140 -0
  91. package/dist/ui/client/utils/events.d.ts +19 -0
  92. package/dist/ui/client/utils/events.d.ts.map +1 -0
  93. package/dist/ui/client/utils/events.js +54 -0
  94. package/dist/ui/client/utils/events.js.map +1 -0
  95. package/dist/ui/client/utils/formatting.d.ts +11 -0
  96. package/dist/ui/client/utils/formatting.d.ts.map +1 -0
  97. package/dist/ui/client/utils/formatting.js +104 -0
  98. package/dist/ui/client/utils/formatting.js.map +1 -0
  99. package/dist/ui/client/utils/router.d.ts +25 -0
  100. package/dist/ui/client/utils/router.d.ts.map +1 -0
  101. package/dist/ui/client/utils/router.js +90 -0
  102. package/dist/ui/client/utils/router.js.map +1 -0
  103. package/dist/ui/guiServer.d.ts +11 -5
  104. package/dist/ui/guiServer.d.ts.map +1 -1
  105. package/dist/ui/guiServer.js +180 -501
  106. package/dist/ui/guiServer.js.map +1 -1
  107. package/dist/ui/menu.js +18 -27
  108. package/dist/ui/menu.js.map +1 -1
  109. package/dist/ui/prompts.js +34 -47
  110. package/dist/ui/prompts.js.map +1 -1
  111. package/dist/ui/server/middleware/errorHandler.d.ts +19 -0
  112. package/dist/ui/server/middleware/errorHandler.d.ts.map +1 -0
  113. package/dist/ui/server/middleware/errorHandler.js +100 -0
  114. package/dist/ui/server/middleware/errorHandler.js.map +1 -0
  115. package/dist/ui/server/routes/apps.d.ts +8 -0
  116. package/dist/ui/server/routes/apps.d.ts.map +1 -0
  117. package/dist/ui/server/routes/apps.js +74 -0
  118. package/dist/ui/server/routes/apps.js.map +1 -0
  119. package/dist/ui/server/routes/dashboard.d.ts +4 -0
  120. package/dist/ui/server/routes/dashboard.d.ts.map +1 -0
  121. package/dist/ui/server/routes/dashboard.js +57 -0
  122. package/dist/ui/server/routes/dashboard.js.map +1 -0
  123. package/dist/ui/server/routes/settings.d.ts +6 -0
  124. package/dist/ui/server/routes/settings.d.ts.map +1 -0
  125. package/dist/ui/server/routes/settings.js +31 -0
  126. package/dist/ui/server/routes/settings.js.map +1 -0
  127. package/dist/ui/server/services/appService.d.ts +45 -0
  128. package/dist/ui/server/services/appService.d.ts.map +1 -0
  129. package/dist/ui/server/services/appService.js +114 -0
  130. package/dist/ui/server/services/appService.js.map +1 -0
  131. package/dist/ui/server/services/removalService.d.ts +24 -0
  132. package/dist/ui/server/services/removalService.d.ts.map +1 -0
  133. package/dist/ui/server/services/removalService.js +83 -0
  134. package/dist/ui/server/services/removalService.js.map +1 -0
  135. package/dist/utils/filesystem.js +32 -49
  136. package/dist/utils/filesystem.js.map +1 -1
  137. package/dist/utils/logger.js +9 -18
  138. package/dist/utils/logger.js.map +1 -1
  139. package/dist/utils/platform.js +10 -22
  140. package/dist/utils/platform.js.map +1 -1
  141. package/dist/utils/upgrade.d.ts +2 -1
  142. package/dist/utils/upgrade.d.ts.map +1 -1
  143. package/dist/utils/upgrade.js +24 -15
  144. package/dist/utils/upgrade.js.map +1 -1
  145. package/package.json +4 -2
  146. package/scripts/publish-npm.sh +64 -0
  147. package/src/core/appUpdateChecker.ts +1 -1
  148. package/src/core/detector.ts +6 -6
  149. package/src/core/duplicateFileFinder.ts +1 -1
  150. package/src/core/orphanedDependencyDetector.ts +2 -2
  151. package/src/core/performanceOptimizer.ts +1 -1
  152. package/src/core/permissionHandler.ts +2 -2
  153. package/src/core/pluginSystem.ts +1 -1
  154. package/src/core/removalRecorder.ts +2 -2
  155. package/src/core/remover.ts +11 -11
  156. package/src/core/reportGenerator.ts +2 -2
  157. package/src/core/scheduledCleanup.ts +2 -2
  158. package/src/core/serviceFileDetector.ts +2 -2
  159. package/src/core/verificationModule.ts +2 -2
  160. package/src/index.ts +8 -8
  161. package/src/managers/brewManager.ts +3 -3
  162. package/src/managers/customManager.ts +2 -2
  163. package/src/managers/linuxManager.ts +3 -3
  164. package/src/managers/npmManager.ts +3 -3
  165. package/src/ui/client/api/client.ts +168 -0
  166. package/src/ui/client/app.ts +125 -0
  167. package/src/ui/client/index.html +107 -0
  168. package/src/ui/client/pages/appDetails.ts +356 -0
  169. package/src/ui/client/pages/appSearch.ts +283 -0
  170. package/src/ui/client/pages/dashboard.ts +211 -0
  171. package/src/ui/client/pages/settings.ts +342 -0
  172. package/src/ui/client/state/appStore.ts +181 -0
  173. package/src/ui/client/state/dashboardStore.ts +123 -0
  174. package/src/ui/client/state/uiStore.ts +166 -0
  175. package/src/ui/client/styles/animations.css +349 -0
  176. package/src/ui/client/styles/base.css +214 -0
  177. package/src/ui/client/styles/components.css +400 -0
  178. package/src/ui/client/styles/layout.css +224 -0
  179. package/src/ui/client/styles/variables.css +140 -0
  180. package/src/ui/client/utils/events.ts +74 -0
  181. package/src/ui/client/utils/formatting.ts +157 -0
  182. package/src/ui/client/utils/router.ts +161 -0
  183. package/src/ui/guiServer.ts +245 -498
  184. package/src/ui/prompts.ts +1 -1
  185. package/src/ui/server/middleware/errorHandler.ts +174 -0
  186. package/src/ui/server/routes/apps.ts +132 -0
  187. package/src/ui/server/routes/dashboard.ts +93 -0
  188. package/src/ui/server/routes/settings.ts +63 -0
  189. package/src/ui/server/services/appService.ts +184 -0
  190. package/src/ui/server/services/removalService.ts +138 -0
  191. package/src/utils/upgrade.ts +19 -2
  192. package/tsconfig.json +3 -2
  193. package/INDEX.md +0 -165
  194. /package/{ACTION_CHECKLIST.md → MD_Files/ACTION_CHECKLIST.md} +0 -0
  195. /package/{APPCLEAN_SUMMARY.md → MD_Files/APPCLEAN_SUMMARY.md} +0 -0
  196. /package/{CHANGELOG.md → MD_Files/CHANGELOG.md} +0 -0
  197. /package/{CODE_OF_CONDUCT.md → MD_Files/CODE_OF_CONDUCT.md} +0 -0
  198. /package/{CODE_REVIEW_REPORT.md → MD_Files/CODE_REVIEW_REPORT.md} +0 -0
  199. /package/{COMMUNITY_POSTS.md → MD_Files/COMMUNITY_POSTS.md} +0 -0
  200. /package/{DEPLOYMENT_GUIDE.md → MD_Files/DEPLOYMENT_GUIDE.md} +0 -0
  201. /package/{DEPLOYMENT_STATUS.md → MD_Files/DEPLOYMENT_STATUS.md} +0 -0
  202. /package/{EXECUTIVE_REPORT.md → MD_Files/EXECUTIVE_REPORT.md} +0 -0
  203. /package/{GITHUB_OPTIMIZATION.md → MD_Files/GITHUB_OPTIMIZATION.md} +0 -0
  204. /package/{MARKETING_SUMMARY.md → MD_Files/MARKETING_SUMMARY.md} +0 -0
  205. /package/{NPM_PACKAGE_OPTIMIZATION.md → MD_Files/NPM_PACKAGE_OPTIMIZATION.md} +0 -0
  206. /package/{NPM_PUBLISH.md → MD_Files/NPM_PUBLISH.md} +0 -0
  207. /package/{PROJECT_SUMMARY.txt → MD_Files/PROJECT_SUMMARY.txt} +0 -0
  208. /package/{PUBLICATION_SUCCESS_REPORT.md → MD_Files/PUBLICATION_SUCCESS_REPORT.md} +0 -0
  209. /package/{QUICKSTART.md → MD_Files/QUICKSTART.md} +0 -0
  210. /package/{SETUP_GITHUB.md → MD_Files/SETUP_GITHUB.md} +0 -0
  211. /package/{TESTING_SUMMARY.md → MD_Files/TESTING_SUMMARY.md} +0 -0
  212. /package/{setup-github.sh → MD_Files/setup-github.sh} +0 -0
@@ -1,540 +1,219 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GUIServer = void 0;
4
- const http_1 = require("http");
5
- const logger_1 = require("../utils/logger");
6
- const upgrade_1 = require("../utils/upgrade");
7
- class GUIServer {
1
+ import { createServer } from 'http';
2
+ import { readFileSync, existsSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname } from 'path';
6
+ import { execFile } from 'child_process';
7
+ import { Logger } from '../utils/logger.js';
8
+ import { sendError } from './server/middleware/errorHandler.js';
9
+ import { handleAppRoutes } from './server/routes/apps.js';
10
+ import { handleDashboardRoutes } from './server/routes/dashboard.js';
11
+ import { handleSettingsRoutes } from './server/routes/settings.js';
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = dirname(__filename);
14
+ export class GUIServer {
8
15
  constructor(port = 3000) {
9
16
  this.port = 3000;
17
+ this.spaHtml = null;
18
+ this.browserProcess = null;
10
19
  this.port = port;
11
- this.upgradeManager = new upgrade_1.UpgradeManager();
12
20
  }
13
21
  async start() {
14
- logger_1.Logger.info(`Starting AppClean GUI server on port ${this.port}...`);
15
- this.server = (0, http_1.createServer)((req, res) => {
16
- if (req.url === '/') {
17
- res.writeHead(200, { 'Content-Type': 'text/html' });
18
- res.end(this.getIndexHTML());
19
- }
20
- else if (req.url?.startsWith('/api/')) {
21
- this.handleAPIRequest(req, res);
22
- }
23
- else {
24
- res.writeHead(404, { 'Content-Type': 'text/plain' });
25
- res.end('Not Found');
26
- }
22
+ Logger.info(`Starting AppClean GUI server on port ${this.port}...`);
23
+ this.loadSPAHtml();
24
+ this.server = createServer((req, res) => {
25
+ this.handleRequest(req, res);
27
26
  });
28
- this.server.listen(this.port, () => {
29
- logger_1.Logger.success(`GUI server running at http://localhost:${this.port}`);
27
+ return new Promise((resolve) => {
28
+ this.server.listen(this.port, async () => {
29
+ Logger.success(`✨ AppClean GUI running at http://localhost:${this.port}`);
30
+ Logger.info('Press Ctrl+C to stop the server');
31
+ await this.openBrowser();
32
+ resolve();
33
+ });
30
34
  });
31
35
  }
32
36
  async stop() {
37
+ await this.closeBrowser();
33
38
  if (this.server) {
34
39
  this.server.close();
35
- logger_1.Logger.info('GUI server stopped');
40
+ Logger.info('GUI server stopped');
36
41
  }
37
42
  }
38
- async handleAPIRequest(req, res) {
39
- const url = req.url || '';
43
+ async openBrowser() {
44
+ const url = `http://localhost:${this.port}`;
40
45
  try {
41
- if (url === '/api/version') {
42
- await this.handleVersionCheck(res);
46
+ const platform = process.platform;
47
+ if (platform === 'darwin') {
48
+ this.browserProcess = execFile('open', [url]);
49
+ }
50
+ else if (platform === 'win32') {
51
+ this.browserProcess = execFile('cmd', ['/c', 'start', url]);
43
52
  }
44
- else if (url === '/api/upgrade') {
45
- await this.handleUpgrade(res);
53
+ else {
54
+ this.browserProcess = execFile('xdg-open', [url]);
46
55
  }
47
- else if (url === '/api/uninstall') {
48
- await this.handleUninstall(res);
56
+ this.browserProcess.on('error', (error) => {
57
+ Logger.warn(`Could not open browser: ${error.message}`);
58
+ });
59
+ }
60
+ catch (error) {
61
+ Logger.warn(`Failed to open browser: ${error.message}`);
62
+ }
63
+ }
64
+ async closeBrowser() {
65
+ if (!this.browserProcess) {
66
+ return;
67
+ }
68
+ try {
69
+ const platform = process.platform;
70
+ if (platform === 'win32') {
71
+ if (this.browserProcess.pid) {
72
+ process.kill(this.browserProcess.pid, 'SIGTERM');
73
+ }
49
74
  }
50
75
  else {
51
- res.writeHead(404, { 'Content-Type': 'application/json' });
52
- res.end(JSON.stringify({ error: 'API endpoint not found' }));
76
+ if (this.browserProcess.pid) {
77
+ process.kill(this.browserProcess.pid);
78
+ }
53
79
  }
80
+ this.browserProcess = null;
54
81
  }
55
82
  catch (error) {
56
- res.writeHead(500, { 'Content-Type': 'application/json' });
57
- res.end(JSON.stringify({ error: error.message }));
83
+ Logger.debug(`Note: Could not close browser process: ${error.message}`);
58
84
  }
59
85
  }
60
- async handleVersionCheck(res) {
61
- const versionInfo = await this.upgradeManager.checkForUpdates();
62
- res.writeHead(200, { 'Content-Type': 'application/json' });
63
- res.end(JSON.stringify(versionInfo));
86
+ async handleRequest(req, res) {
87
+ const url = req.url || '/';
88
+ const method = req.method || 'GET';
89
+ const pathname = url.split('?')[0];
90
+ this.setCORSHeaders(res);
91
+ if (method === 'OPTIONS') {
92
+ res.writeHead(200);
93
+ res.end();
94
+ return;
95
+ }
96
+ try {
97
+ if (pathname.startsWith('/api/')) {
98
+ return this.handleAPIRequest(method, pathname, req, res);
99
+ }
100
+ if (pathname.startsWith('/static/')) {
101
+ return this.serveStaticAsset(pathname, res);
102
+ }
103
+ this.serveSPA(res);
104
+ }
105
+ catch (error) {
106
+ Logger.error(`Request error: ${error.message}`);
107
+ sendError(res, 'Internal server error', 500);
108
+ }
64
109
  }
65
- async handleUpgrade(res) {
66
- const result = await this.upgradeManager.upgrade();
67
- res.writeHead(result.success ? 200 : 500, {
68
- 'Content-Type': 'application/json',
69
- });
70
- res.end(JSON.stringify(result));
110
+ handleAPIRequest(method, pathname, req, res) {
111
+ if (handleAppRoutes(method, pathname, req, res)) {
112
+ return;
113
+ }
114
+ if (handleDashboardRoutes(method, pathname, req, res)) {
115
+ return;
116
+ }
117
+ if (handleSettingsRoutes(method, pathname, req, res)) {
118
+ return;
119
+ }
120
+ sendError(res, 'API endpoint not found', 404);
71
121
  }
72
- async handleUninstall(res) {
73
- const result = await this.upgradeManager.uninstall();
74
- res.writeHead(result.success ? 200 : 500, {
75
- 'Content-Type': 'application/json',
76
- });
77
- res.end(JSON.stringify(result));
122
+ serveStaticAsset(pathname, res) {
123
+ const relativePath = pathname.slice(8);
124
+ if (relativePath.includes('..')) {
125
+ sendError(res, 'Access denied', 403);
126
+ return;
127
+ }
128
+ const filePath = join(__dirname, 'client', relativePath);
129
+ if (!existsSync(filePath)) {
130
+ sendError(res, 'Asset not found', 404);
131
+ return;
132
+ }
133
+ try {
134
+ const content = readFileSync(filePath);
135
+ const contentType = this.getContentType(filePath);
136
+ res.writeHead(200, {
137
+ 'Content-Type': contentType,
138
+ 'Cache-Control': 'public, max-age=3600',
139
+ });
140
+ res.end(content);
141
+ }
142
+ catch (error) {
143
+ Logger.warn(`Failed to serve asset ${pathname}: ${error.message}`);
144
+ sendError(res, 'Failed to load asset', 500);
145
+ }
78
146
  }
79
- getIndexHTML() {
80
- return `
147
+ serveSPA(res) {
148
+ if (!this.spaHtml) {
149
+ const fallbackHtml = `
81
150
  <!DOCTYPE html>
82
- <html lang="en">
151
+ <html>
83
152
  <head>
84
153
  <meta charset="UTF-8">
85
154
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
86
- <title>AppClean GUI - v1.2.0</title>
87
- <style>
88
- * {
89
- margin: 0;
90
- padding: 0;
91
- box-sizing: border-box;
92
- }
93
- body {
94
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
95
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
96
- min-height: 100vh;
97
- display: flex;
98
- align-items: center;
99
- justify-content: center;
100
- padding: 20px;
101
- }
102
- .container {
103
- background: white;
104
- border-radius: 12px;
105
- box-shadow: 0 20px 60px rgba(0,0,0,0.3);
106
- max-width: 800px;
107
- width: 100%;
108
- padding: 40px;
109
- }
110
- h1 {
111
- color: #333;
112
- margin-bottom: 10px;
113
- }
114
- h3 {
115
- color: #333;
116
- margin-top: 20px;
117
- margin-bottom: 15px;
118
- }
119
- .version {
120
- color: #666;
121
- font-size: 14px;
122
- margin-bottom: 30px;
123
- }
124
- .feature-badge {
125
- display: inline-block;
126
- background: #667eea;
127
- color: white;
128
- padding: 8px 16px;
129
- border-radius: 20px;
130
- font-size: 12px;
131
- font-weight: 600;
132
- margin-bottom: 20px;
133
- }
134
- p {
135
- color: #666;
136
- line-height: 1.6;
137
- margin-bottom: 20px;
138
- }
139
- .section {
140
- background: #f0f4ff;
141
- border-left: 4px solid #667eea;
142
- padding: 20px;
143
- border-radius: 4px;
144
- margin-top: 30px;
145
- }
146
- .version-info {
147
- background: #f9f9f9;
148
- padding: 15px;
149
- border-radius: 6px;
150
- margin: 15px 0;
151
- font-family: monospace;
152
- font-size: 14px;
153
- }
154
- .version-row {
155
- display: flex;
156
- justify-content: space-between;
157
- margin: 8px 0;
158
- }
159
- .label {
160
- color: #666;
161
- font-weight: 500;
162
- }
163
- .value {
164
- color: #333;
165
- font-weight: 600;
166
- }
167
- .update-available {
168
- color: #f59e0b;
169
- font-weight: 600;
170
- }
171
- .up-to-date {
172
- color: #10b981;
173
- font-weight: 600;
174
- }
175
- .button-group {
176
- display: flex;
177
- gap: 10px;
178
- margin-top: 20px;
179
- }
180
- button {
181
- flex: 1;
182
- padding: 12px 20px;
183
- border: none;
184
- border-radius: 6px;
185
- font-size: 14px;
186
- font-weight: 600;
187
- cursor: pointer;
188
- transition: all 0.3s ease;
189
- }
190
- .btn-primary {
191
- background: #667eea;
192
- color: white;
193
- }
194
- .btn-primary:hover {
195
- background: #5568d3;
196
- transform: translateY(-2px);
197
- box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
198
- }
199
- .btn-primary:disabled {
200
- background: #ccc;
201
- cursor: not-allowed;
202
- transform: none;
203
- }
204
- .btn-secondary {
205
- background: #e5e7eb;
206
- color: #333;
207
- }
208
- .btn-secondary:hover {
209
- background: #d1d5db;
210
- }
211
- .status-message {
212
- margin-top: 15px;
213
- padding: 12px;
214
- border-radius: 6px;
215
- display: none;
216
- font-weight: 500;
217
- }
218
- .status-success {
219
- background: #d1fae5;
220
- color: #065f46;
221
- display: block;
222
- }
223
- .status-error {
224
- background: #fee2e2;
225
- color: #991b1b;
226
- display: block;
227
- }
228
- .status-loading {
229
- background: #dbeafe;
230
- color: #1e40af;
231
- display: block;
232
- }
233
- .spinner {
234
- display: inline-block;
235
- width: 16px;
236
- height: 16px;
237
- border: 2px solid rgba(30, 64, 175, 0.3);
238
- border-top-color: #1e40af;
239
- border-radius: 50%;
240
- animation: spin 0.8s linear infinite;
241
- margin-right: 8px;
242
- vertical-align: middle;
243
- }
244
- @keyframes spin {
245
- to { transform: rotate(360deg); }
246
- }
247
- ul {
248
- margin-left: 20px;
249
- margin-top: 10px;
250
- }
251
- .danger-zone {
252
- background: #fee2e2;
253
- border-left: 4px solid #dc2626;
254
- padding: 20px;
255
- border-radius: 4px;
256
- margin-top: 30px;
257
- }
258
- .danger-zone h3 {
259
- color: #991b1b;
260
- margin-top: 0;
261
- }
262
- .danger-zone p {
263
- color: #7c2d12;
264
- margin-bottom: 15px;
265
- }
266
- .btn-danger {
267
- background: #dc2626;
268
- color: white;
269
- }
270
- .btn-danger:hover {
271
- background: #b91c1c;
272
- transform: translateY(-2px);
273
- box-shadow: 0 5px 15px rgba(220, 38, 38, 0.4);
274
- }
275
- .btn-danger:disabled {
276
- background: #ccc;
277
- cursor: not-allowed;
278
- transform: none;
279
- }
280
- /* Modal Styles */
281
- .modal {
282
- display: none;
283
- position: fixed;
284
- z-index: 1000;
285
- left: 0;
286
- top: 0;
287
- width: 100%;
288
- height: 100%;
289
- background-color: rgba(0,0,0,0.5);
290
- }
291
- .modal-content {
292
- background-color: white;
293
- margin: auto;
294
- padding: 30px;
295
- border-radius: 12px;
296
- width: 90%;
297
- max-width: 500px;
298
- position: absolute;
299
- top: 50%;
300
- left: 50%;
301
- transform: translate(-50%, -50%);
302
- box-shadow: 0 20px 60px rgba(0,0,0,0.3);
303
- }
304
- .modal-content h2 {
305
- color: #dc2626;
306
- margin-bottom: 15px;
307
- }
308
- .modal-content p {
309
- color: #666;
310
- margin-bottom: 20px;
311
- }
312
- .modal-buttons {
313
- display: flex;
314
- gap: 10px;
315
- justify-content: flex-end;
316
- }
317
- .modal-buttons button {
318
- padding: 10px 20px;
319
- border: none;
320
- border-radius: 6px;
321
- font-weight: 600;
322
- cursor: pointer;
323
- transition: all 0.3s ease;
324
- }
325
- .modal-buttons .btn-cancel {
326
- background: #e5e7eb;
327
- color: #333;
328
- }
329
- .modal-buttons .btn-cancel:hover {
330
- background: #d1d5db;
331
- }
332
- .modal-buttons .btn-confirm {
333
- background: #dc2626;
334
- color: white;
335
- }
336
- .modal-buttons .btn-confirm:hover {
337
- background: #b91c1c;
338
- }
339
- </style>
155
+ <title>AppClean</title>
340
156
  </head>
341
- <body>
342
- <div class="container">
343
- <h1>🎨 AppClean GUI</h1>
344
- <p class="version">v1.2.0 - GUI Application Feature</p>
345
- <span class="feature-badge">Coming Soon</span>
346
-
347
- <p>
348
- A beautiful, intuitive graphical user interface for AppClean, bringing the power of
349
- intelligent app removal to users who prefer a visual interface.
350
- </p>
351
-
352
- <div class="section">
353
- <h3>📦 Version & Updates</h3>
354
- <div class="version-info">
355
- <div class="version-row">
356
- <span class="label">Current Version:</span>
357
- <span class="value" id="currentVersion">Loading...</span>
358
- </div>
359
- <div class="version-row">
360
- <span class="label">Latest Version:</span>
361
- <span class="value" id="latestVersion">Loading...</span>
362
- </div>
363
- <div class="version-row">
364
- <span class="label">Status:</span>
365
- <span id="updateStatus">Checking...</span>
366
- </div>
367
- </div>
368
- <div class="button-group">
369
- <button class="btn-secondary" onclick="checkVersion()">🔄 Check for Updates</button>
370
- <button class="btn-primary" id="upgradeBtn" onclick="upgradeAppClean()" disabled>⬆️ Upgrade</button>
371
- </div>
372
- <div id="statusMessage" class="status-message"></div>
373
- </div>
374
-
375
- <div class="section">
376
- <h3>🚀 Features Coming in v1.2.0</h3>
377
- <ul>
378
- <li>✨ Modern, responsive GUI design</li>
379
- <li>🖥️ Cross-platform support (macOS, Linux, Windows)</li>
380
- <li>🔍 Visual app search and discovery</li>
381
- <li>📊 Beautiful artifact visualization</li>
382
- <li>🗑️ Drag-and-drop app removal</li>
383
- <li>📈 Real-time removal progress</li>
384
- <li>📋 Interactive report viewer</li>
385
- </ul>
386
- </div>
387
-
388
- <div class="danger-zone">
389
- <h3>⚠️ Danger Zone</h3>
390
- <p>
391
- Uninstall AppClean from your system. This action will remove the application and
392
- all its global files. This cannot be undone easily.
393
- </p>
394
- <div class="button-group">
395
- <button class="btn-danger" onclick="showUninstallConfirm()">🗑️ Uninstall AppClean</button>
396
- </div>
397
- </div>
398
- </div>
399
-
400
- <!-- Uninstall Confirmation Modal -->
401
- <div id="uninstallModal" class="modal">
402
- <div class="modal-content">
403
- <h2>⚠️ Confirm Uninstall</h2>
404
- <p>
405
- Are you sure you want to uninstall AppClean? This action will remove the application
406
- from your system and cannot be easily undone.
407
- </p>
408
- <p style="color: #dc2626; font-weight: 600;">
409
- This action cannot be undone!
410
- </p>
411
- <div class="modal-buttons">
412
- <button class="btn-cancel" onclick="closeUninstallConfirm()">Cancel</button>
413
- <button class="btn-confirm" onclick="confirmUninstall()">Uninstall</button>
414
- </div>
415
- </div>
416
- </div>
417
-
418
- <script>
419
- // Check version on page load
420
- window.addEventListener('load', checkVersion);
421
-
422
- async function checkVersion() {
423
- const statusEl = document.getElementById('statusMessage');
424
- statusEl.textContent = '🔄 Checking for updates...';
425
- statusEl.className = 'status-message status-loading';
426
- statusEl.innerHTML = '<span class="spinner"></span>Checking for updates...';
427
-
428
- try {
429
- const response = await fetch('/api/version');
430
- const data = await response.json();
431
-
432
- document.getElementById('currentVersion').textContent = 'v' + data.current;
433
- document.getElementById('latestVersion').textContent = 'v' + data.latest;
434
-
435
- const upgradeBtn = document.getElementById('upgradeBtn');
436
- const updateStatus = document.getElementById('updateStatus');
437
-
438
- if (data.isUpdateAvailable) {
439
- updateStatus.innerHTML = '<span class="update-available">⚠️ Update available!</span>';
440
- upgradeBtn.disabled = false;
441
- statusEl.textContent = '✓ Update available! Click the Upgrade button to install.';
442
- statusEl.className = 'status-message status-success';
443
- } else {
444
- updateStatus.innerHTML = '<span class="up-to-date">✓ Up to date</span>';
445
- upgradeBtn.disabled = true;
446
- statusEl.textContent = '✓ AppClean is already up to date!';
447
- statusEl.className = 'status-message status-success';
157
+ <body style="font-family: system-ui; padding: 20px; text-align: center;">
158
+ <h1>⚠️ GUI Not Ready</h1>
159
+ <p>The SPA assets haven't been compiled yet.</p>
160
+ <p>Run <code>npm run build</code> to compile the TypeScript/CSS files.</p>
161
+ <p>For now, use the CLI: <code>appclean --help</code></p>
162
+ </body>
163
+ </html>`;
164
+ res.writeHead(200, { 'Content-Type': 'text/html' });
165
+ res.end(fallbackHtml);
166
+ return;
448
167
  }
449
- } catch (error) {
450
- statusEl.textContent = '✗ Failed to check for updates: ' + error.message;
451
- statusEl.className = 'status-message status-error';
452
- }
168
+ res.writeHead(200, {
169
+ 'Content-Type': 'text/html',
170
+ 'Cache-Control': 'no-cache',
171
+ });
172
+ res.end(this.spaHtml);
453
173
  }
454
-
455
- async function upgradeAppClean() {
456
- const statusEl = document.getElementById('statusMessage');
457
- const upgradeBtn = document.getElementById('upgradeBtn');
458
-
459
- upgradeBtn.disabled = true;
460
- statusEl.innerHTML = '<span class="spinner"></span>Upgrading AppClean...';
461
- statusEl.className = 'status-message status-loading';
462
-
463
- try {
464
- const response = await fetch('/api/upgrade');
465
- const data = await response.json();
466
-
467
- if (data.success) {
468
- statusEl.textContent = '✓ ' + data.message;
469
- statusEl.className = 'status-message status-success';
470
- setTimeout(() => {
471
- statusEl.textContent = 'Please refresh the page or restart the GUI server.';
472
- checkVersion();
473
- }, 2000);
474
- } else {
475
- statusEl.textContent = '✗ ' + data.message;
476
- statusEl.className = 'status-message status-error';
477
- upgradeBtn.disabled = false;
174
+ loadSPAHtml() {
175
+ const htmlPath = join(__dirname, 'client', 'index.html');
176
+ try {
177
+ if (existsSync(htmlPath)) {
178
+ this.spaHtml = readFileSync(htmlPath, 'utf-8');
179
+ Logger.debug('✓ Loaded SPA HTML');
180
+ }
181
+ else {
182
+ Logger.warn(`⚠️ SPA HTML not found at ${htmlPath}`);
183
+ Logger.info('Make sure to run: npm run build');
184
+ }
478
185
  }
479
- } catch (error) {
480
- statusEl.textContent = '✗ Upgrade failed: ' + error.message;
481
- statusEl.className = 'status-message status-error';
482
- upgradeBtn.disabled = false;
483
- }
484
- }
485
-
486
- // Uninstall Functions
487
- function showUninstallConfirm() {
488
- document.getElementById('uninstallModal').style.display = 'block';
489
- }
490
-
491
- function closeUninstallConfirm() {
492
- document.getElementById('uninstallModal').style.display = 'none';
493
- }
494
-
495
- async function confirmUninstall() {
496
- const modal = document.getElementById('uninstallModal');
497
- const btn = modal.querySelector('.btn-confirm');
498
-
499
- btn.disabled = true;
500
- btn.textContent = '🗑️ Uninstalling...';
501
-
502
- try {
503
- const response = await fetch('/api/uninstall');
504
- const data = await response.json();
505
-
506
- if (data.success) {
507
- modal.querySelector('p').textContent = '✓ ' + data.message;
508
- modal.querySelector('h2').textContent = '✓ Uninstall Complete';
509
- modal.querySelector('.modal-buttons').innerHTML =
510
- '<button class="btn-cancel" onclick="window.close()">Close</button>';
511
- setTimeout(() => {
512
- alert('AppClean has been uninstalled. You can close this window.');
513
- }, 500);
514
- } else {
515
- alert('❌ Uninstall failed: ' + data.message);
516
- btn.disabled = false;
517
- btn.textContent = 'Uninstall';
186
+ catch (error) {
187
+ Logger.warn(`Failed to load SPA HTML: ${error.message}`);
518
188
  }
519
- } catch (error) {
520
- alert('❌ Error: ' + error.message);
521
- btn.disabled = false;
522
- btn.textContent = 'Uninstall';
523
- }
524
189
  }
525
-
526
- // Close modal when clicking outside
527
- window.onclick = function(event) {
528
- const modal = document.getElementById('uninstallModal');
529
- if (event.target === modal) {
530
- modal.style.display = 'none';
531
- }
532
- }
533
- </script>
534
- </body>
535
- </html>
536
- `;
190
+ getContentType(filePath) {
191
+ const ext = filePath.toLowerCase().split('.').pop();
192
+ const typeMap = {
193
+ 'js': 'application/javascript; charset=utf-8',
194
+ 'css': 'text/css; charset=utf-8',
195
+ 'html': 'text/html; charset=utf-8',
196
+ 'json': 'application/json; charset=utf-8',
197
+ 'svg': 'image/svg+xml',
198
+ 'png': 'image/png',
199
+ 'jpg': 'image/jpeg',
200
+ 'jpeg': 'image/jpeg',
201
+ 'gif': 'image/gif',
202
+ 'ico': 'image/x-icon',
203
+ 'webp': 'image/webp',
204
+ 'woff': 'font/woff',
205
+ 'woff2': 'font/woff2',
206
+ 'ttf': 'font/ttf',
207
+ 'eot': 'application/vnd.ms-fontobject',
208
+ };
209
+ return typeMap[ext || ''] || 'application/octet-stream';
210
+ }
211
+ setCORSHeaders(res) {
212
+ res.setHeader('Access-Control-Allow-Origin', '*');
213
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
214
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
215
+ res.setHeader('Access-Control-Max-Age', '3600');
537
216
  }
538
217
  }
539
- exports.GUIServer = GUIServer;
218
+ export default GUIServer;
540
219
  //# sourceMappingURL=guiServer.js.map