projax 3.3.39 → 3.3.51
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/README.md +73 -0
- package/dist/__tests__/port-scanner.test.js +7 -17
- package/dist/__tests__/script-runner.test.js +7 -17
- package/dist/api/__tests__/database.test.js +7 -17
- package/dist/api/__tests__/database.test.js.map +1 -1
- package/dist/api/__tests__/routes.test.js +7 -17
- package/dist/api/__tests__/routes.test.js.map +1 -1
- package/dist/api/__tests__/scanner.test.js +7 -17
- package/dist/api/__tests__/scanner.test.js.map +1 -1
- package/dist/api/database.d.ts +20 -1
- package/dist/api/database.d.ts.map +1 -1
- package/dist/api/database.js +192 -21
- package/dist/api/database.js.map +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +17 -19
- package/dist/api/index.js.map +1 -1
- package/dist/api/migrate.js +2 -1
- package/dist/api/migrate.js.map +1 -1
- package/dist/api/package.json +3 -2
- package/dist/api/routes/backup.d.ts +3 -0
- package/dist/api/routes/backup.d.ts.map +1 -0
- package/dist/api/routes/backup.js +51 -0
- package/dist/api/routes/backup.js.map +1 -0
- package/dist/api/routes/index.d.ts.map +1 -1
- package/dist/api/routes/index.js +6 -0
- package/dist/api/routes/index.js.map +1 -1
- package/dist/api/routes/mcp.d.ts +3 -0
- package/dist/api/routes/mcp.d.ts.map +1 -0
- package/dist/api/routes/mcp.js +147 -0
- package/dist/api/routes/mcp.js.map +1 -0
- package/dist/api/routes/projects.d.ts.map +1 -1
- package/dist/api/routes/projects.js +27 -17
- package/dist/api/routes/projects.js.map +1 -1
- package/dist/api/routes/settings.d.ts.map +1 -1
- package/dist/api/routes/settings.js +119 -11
- package/dist/api/routes/settings.js.map +1 -1
- package/dist/api/routes/workspaces.d.ts +3 -0
- package/dist/api/routes/workspaces.d.ts.map +1 -0
- package/dist/api/routes/workspaces.js +504 -0
- package/dist/api/routes/workspaces.js.map +1 -0
- package/dist/api/services/scanner.js +10 -19
- package/dist/api/services/scanner.js.map +1 -1
- package/dist/api/services/test-parser.js +3 -2
- package/dist/api/services/test-parser.js.map +1 -1
- package/dist/api/types.d.ts +31 -0
- package/dist/api/types.d.ts.map +1 -1
- package/dist/core/__tests__/database.test.js +7 -17
- package/dist/core/__tests__/detector.test.js +7 -17
- package/dist/core/__tests__/index.test.js +7 -17
- package/dist/core/__tests__/scanner.test.js +7 -17
- package/dist/core/__tests__/settings.test.js +7 -17
- package/dist/core/backup-utils.d.ts +17 -0
- package/dist/core/backup-utils.js +157 -0
- package/dist/core/database.d.ts +1 -0
- package/dist/core/database.js +9 -18
- package/dist/core/detector.js +11 -21
- package/dist/core/git-utils.d.ts +12 -0
- package/dist/core/git-utils.js +87 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +8 -5
- package/dist/core/scanner.js +3 -2
- package/dist/core/settings.d.ts +85 -0
- package/dist/core/settings.js +306 -9
- package/dist/core/workspace-utils.d.ts +37 -0
- package/dist/core/workspace-utils.js +143 -0
- package/dist/core-bridge.js +7 -17
- package/dist/electron/core/__tests__/database.test.js +7 -17
- package/dist/electron/core/__tests__/detector.test.js +7 -17
- package/dist/electron/core/__tests__/index.test.js +7 -17
- package/dist/electron/core/__tests__/scanner.test.js +7 -17
- package/dist/electron/core/__tests__/settings.test.js +7 -17
- package/dist/electron/core/backup-utils.d.ts +17 -0
- package/dist/electron/core/backup-utils.js +157 -0
- package/dist/electron/core/database.d.ts +1 -0
- package/dist/electron/core/database.js +9 -18
- package/dist/electron/core/detector.js +11 -21
- package/dist/electron/core/git-utils.d.ts +12 -0
- package/dist/electron/core/git-utils.js +87 -0
- package/dist/electron/core/index.d.ts +3 -0
- package/dist/electron/core/index.js +8 -5
- package/dist/electron/core/scanner.js +3 -2
- package/dist/electron/core/settings.d.ts +85 -0
- package/dist/electron/core/settings.js +306 -9
- package/dist/electron/core/workspace-utils.d.ts +37 -0
- package/dist/electron/core/workspace-utils.js +143 -0
- package/dist/electron/core.js +7 -17
- package/dist/electron/main.js +663 -33
- package/dist/electron/port-extractor.js +9 -18
- package/dist/electron/port-scanner.js +11 -20
- package/dist/electron/port-utils.js +5 -4
- package/dist/electron/preload.d.ts +27 -2
- package/dist/electron/preload.js +18 -2
- package/dist/electron/renderer/assets/index-B-etDnj2.js +64 -0
- package/dist/electron/renderer/assets/index-Bx18Cyic.js +64 -0
- package/dist/electron/renderer/assets/index-C8f5yNYe.js +64 -0
- package/dist/electron/renderer/assets/index-CIZ3Wl6c.css +1 -0
- package/dist/electron/renderer/assets/index-CJbsU9y8.css +1 -0
- package/dist/electron/renderer/assets/index-CopVNRnR.js +64 -0
- package/dist/electron/renderer/assets/index-DUvcepWm.js +64 -0
- package/dist/electron/renderer/assets/index-DWe2TQFv.css +1 -0
- package/dist/electron/renderer/assets/index-DZzB20Xf.css +1 -0
- package/dist/electron/renderer/assets/index-DknLdADV.js +63 -0
- package/dist/electron/renderer/assets/index-DocuD8Lk.js +64 -0
- package/dist/electron/renderer/assets/index-DyU-xfd8.css +1 -0
- package/dist/electron/renderer/assets/index-GwC-JVUy.css +1 -0
- package/dist/electron/renderer/assets/index-fehviker.js +63 -0
- package/dist/electron/renderer/index.html +2 -2
- package/dist/electron/script-runner.js +20 -29
- package/dist/index.js +395 -21
- package/dist/port-extractor.js +9 -18
- package/dist/port-scanner.js +11 -20
- package/dist/port-utils.js +5 -4
- package/dist/script-runner.js +20 -29
- package/dist/test-parser.js +3 -2
- package/package.json +3 -2
package/dist/electron/main.js
CHANGED
|
@@ -15,27 +15,19 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) ||
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
35
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
26
|
const electron_1 = require("electron");
|
|
37
27
|
const path = __importStar(require("path"));
|
|
38
28
|
const fs = __importStar(require("fs"));
|
|
29
|
+
const os = __importStar(require("os"));
|
|
30
|
+
const http = __importStar(require("http"));
|
|
39
31
|
const child_process_1 = require("child_process");
|
|
40
32
|
const tail_1 = require("tail");
|
|
41
33
|
const core_1 = require("./core");
|
|
@@ -68,17 +60,53 @@ else {
|
|
|
68
60
|
height: 800,
|
|
69
61
|
frame: false,
|
|
70
62
|
titleBarStyle: 'hidden',
|
|
63
|
+
trafficLightPosition: { x: -100, y: -100 }, // Hide macOS traffic lights
|
|
71
64
|
title: 'PROJAX UI',
|
|
72
65
|
webPreferences: {
|
|
73
66
|
preload: path.join(__dirname, 'preload.js'),
|
|
74
67
|
nodeIntegration: false,
|
|
75
68
|
contextIsolation: true,
|
|
69
|
+
webSecurity: true,
|
|
76
70
|
},
|
|
71
|
+
show: false, // Don't show until ready
|
|
72
|
+
});
|
|
73
|
+
// Show window when ready to prevent white screen flash
|
|
74
|
+
mainWindow.once('ready-to-show', () => {
|
|
75
|
+
mainWindow?.show();
|
|
77
76
|
});
|
|
78
77
|
// Load the app
|
|
79
78
|
if (isDev) {
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
// Wait for Vite dev server to be ready before loading
|
|
80
|
+
const checkServerAndLoad = (retries = 10) => {
|
|
81
|
+
const req = http.get('http://localhost:7898', (res) => {
|
|
82
|
+
console.log('Vite dev server is ready!');
|
|
83
|
+
mainWindow?.loadURL('http://localhost:7898');
|
|
84
|
+
mainWindow?.webContents.openDevTools();
|
|
85
|
+
});
|
|
86
|
+
req.on('error', (error) => {
|
|
87
|
+
if (retries > 0) {
|
|
88
|
+
console.log(`Vite dev server not ready (${retries} retries left), retrying in 1 second...`);
|
|
89
|
+
setTimeout(() => checkServerAndLoad(retries - 1), 1000);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.error('Failed to connect to Vite dev server after multiple retries');
|
|
93
|
+
console.error('Make sure Vite is running on port 7898');
|
|
94
|
+
mainWindow?.loadURL('http://localhost:7898'); // Try anyway
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
req.setTimeout(2000, () => {
|
|
98
|
+
req.destroy();
|
|
99
|
+
if (retries > 0) {
|
|
100
|
+
console.log(`Vite dev server timeout (${retries} retries left), retrying...`);
|
|
101
|
+
setTimeout(() => checkServerAndLoad(retries - 1), 1000);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.error('Failed to connect to Vite dev server - timeout');
|
|
105
|
+
mainWindow?.loadURL('http://localhost:7898'); // Try anyway
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
checkServerAndLoad();
|
|
82
110
|
}
|
|
83
111
|
else {
|
|
84
112
|
// Try bundled renderer path first (when bundled in CLI: dist/electron/renderer/index.html)
|
|
@@ -113,6 +141,49 @@ else {
|
|
|
113
141
|
mainWindow.on('closed', () => {
|
|
114
142
|
mainWindow = null;
|
|
115
143
|
});
|
|
144
|
+
// Handle page load errors
|
|
145
|
+
mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL) => {
|
|
146
|
+
console.error('Failed to load:', validatedURL, errorCode, errorDescription);
|
|
147
|
+
if (isDev && validatedURL === 'http://localhost:7898/') {
|
|
148
|
+
console.log('Retrying to load Vite dev server...');
|
|
149
|
+
setTimeout(() => {
|
|
150
|
+
mainWindow?.loadURL('http://localhost:7898');
|
|
151
|
+
}, 2000);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
// Log when page finishes loading
|
|
155
|
+
mainWindow.webContents.on('did-finish-load', () => {
|
|
156
|
+
console.log('Page loaded successfully');
|
|
157
|
+
});
|
|
158
|
+
// Log console messages from renderer
|
|
159
|
+
mainWindow.webContents.on('console-message', (event, level, message, line, sourceId) => {
|
|
160
|
+
console.log(`[Renderer ${level}]:`, message, sourceId ? `(${sourceId}:${line})` : '');
|
|
161
|
+
});
|
|
162
|
+
// Log all console output from renderer
|
|
163
|
+
mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL, isMainFrame) => {
|
|
164
|
+
if (isMainFrame) {
|
|
165
|
+
console.error('Main frame failed to load:', {
|
|
166
|
+
errorCode,
|
|
167
|
+
errorDescription,
|
|
168
|
+
validatedURL,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
// Add keyboard shortcut to reload in dev mode
|
|
173
|
+
if (isDev) {
|
|
174
|
+
mainWindow.webContents.on('before-input-event', (event, input) => {
|
|
175
|
+
// Cmd+R or Ctrl+R to reload
|
|
176
|
+
if ((input.control || input.meta) && input.key.toLowerCase() === 'r') {
|
|
177
|
+
event.preventDefault();
|
|
178
|
+
mainWindow?.reload();
|
|
179
|
+
}
|
|
180
|
+
// Cmd+Shift+R or Ctrl+Shift+R to hard reload
|
|
181
|
+
if ((input.control || input.meta) && input.shift && input.key.toLowerCase() === 'r') {
|
|
182
|
+
event.preventDefault();
|
|
183
|
+
mainWindow?.webContents.reloadIgnoringCache();
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
116
187
|
}
|
|
117
188
|
// Start API server
|
|
118
189
|
function startAPIServer() {
|
|
@@ -134,11 +205,18 @@ else {
|
|
|
134
205
|
console.warn('API server not found. Some features may not work.');
|
|
135
206
|
return;
|
|
136
207
|
}
|
|
137
|
-
console.log('Starting API server
|
|
208
|
+
console.log('Starting API server from:', apiPath);
|
|
209
|
+
// Kill any existing API server on the same port first
|
|
210
|
+
if (apiProcess) {
|
|
211
|
+
console.log('Killing existing API server...');
|
|
212
|
+
apiProcess.kill();
|
|
213
|
+
apiProcess = null;
|
|
214
|
+
}
|
|
138
215
|
apiProcess = (0, child_process_1.spawn)('node', [apiPath], {
|
|
139
216
|
detached: false,
|
|
140
217
|
stdio: 'pipe',
|
|
141
218
|
env: { ...process.env },
|
|
219
|
+
cwd: path.dirname(apiPath),
|
|
142
220
|
});
|
|
143
221
|
apiProcess.stdout?.on('data', (data) => {
|
|
144
222
|
console.log(`[API] ${data.toString().trim()}`);
|
|
@@ -149,13 +227,135 @@ else {
|
|
|
149
227
|
apiProcess.on('exit', (code) => {
|
|
150
228
|
console.log(`API server exited with code ${code}`);
|
|
151
229
|
apiProcess = null;
|
|
230
|
+
// Restart API server if it crashes (wait 2 seconds)
|
|
231
|
+
if (code !== 0) {
|
|
232
|
+
console.log('API server crashed, restarting in 2 seconds...');
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
startAPIServer();
|
|
235
|
+
}, 2000);
|
|
236
|
+
}
|
|
152
237
|
});
|
|
153
238
|
}
|
|
154
239
|
catch (error) {
|
|
155
240
|
console.error('Failed to start API server:', error);
|
|
156
241
|
}
|
|
157
242
|
}
|
|
243
|
+
function createMenu() {
|
|
244
|
+
const template = [
|
|
245
|
+
{
|
|
246
|
+
label: 'PROJAX UI',
|
|
247
|
+
submenu: [
|
|
248
|
+
{ role: 'about', label: 'About PROJAX UI' },
|
|
249
|
+
{ type: 'separator' },
|
|
250
|
+
{ role: 'services', label: 'Services' },
|
|
251
|
+
{ type: 'separator' },
|
|
252
|
+
{ role: 'hide', label: 'Hide PROJAX UI' },
|
|
253
|
+
{ role: 'hideOthers', label: 'Hide Others' },
|
|
254
|
+
{ role: 'unhide', label: 'Show All' },
|
|
255
|
+
{ type: 'separator' },
|
|
256
|
+
{ role: 'quit', label: 'Quit PROJAX UI' },
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
label: 'File',
|
|
261
|
+
submenu: [
|
|
262
|
+
{
|
|
263
|
+
label: 'New Project',
|
|
264
|
+
accelerator: 'CmdOrCtrl+N',
|
|
265
|
+
click: () => {
|
|
266
|
+
if (mainWindow) {
|
|
267
|
+
mainWindow.webContents.send('menu-action', 'new-project');
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
label: 'New Workspace',
|
|
273
|
+
accelerator: 'CmdOrCtrl+Shift+N',
|
|
274
|
+
click: () => {
|
|
275
|
+
if (mainWindow) {
|
|
276
|
+
mainWindow.webContents.send('menu-action', 'new-workspace');
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
{ type: 'separator' },
|
|
281
|
+
{
|
|
282
|
+
label: 'Open Workspace',
|
|
283
|
+
accelerator: 'CmdOrCtrl+O',
|
|
284
|
+
click: () => {
|
|
285
|
+
if (mainWindow) {
|
|
286
|
+
mainWindow.webContents.send('menu-action', 'open-workspace');
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
label: 'Open Directory as New Project',
|
|
292
|
+
accelerator: 'CmdOrCtrl+Shift+O',
|
|
293
|
+
click: async () => {
|
|
294
|
+
if (mainWindow) {
|
|
295
|
+
const result = await electron_1.dialog.showOpenDialog(mainWindow, {
|
|
296
|
+
properties: ['openDirectory'],
|
|
297
|
+
});
|
|
298
|
+
if (!result.canceled && result.filePaths.length > 0) {
|
|
299
|
+
mainWindow.webContents.send('menu-action', 'open-directory-as-project', result.filePaths[0]);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
{ type: 'separator' },
|
|
305
|
+
{ role: 'close', label: 'Close Window' },
|
|
306
|
+
],
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
label: 'Edit',
|
|
310
|
+
submenu: [
|
|
311
|
+
{ role: 'undo', label: 'Undo' },
|
|
312
|
+
{ role: 'redo', label: 'Redo' },
|
|
313
|
+
{ type: 'separator' },
|
|
314
|
+
{ role: 'cut', label: 'Cut' },
|
|
315
|
+
{ role: 'copy', label: 'Copy' },
|
|
316
|
+
{ role: 'paste', label: 'Paste' },
|
|
317
|
+
{ role: 'selectAll', label: 'Select All' },
|
|
318
|
+
],
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
label: 'View',
|
|
322
|
+
submenu: [
|
|
323
|
+
{ role: 'reload', label: 'Reload' },
|
|
324
|
+
{ role: 'forceReload', label: 'Force Reload' },
|
|
325
|
+
{ role: 'toggleDevTools', label: 'Toggle Developer Tools' },
|
|
326
|
+
{ type: 'separator' },
|
|
327
|
+
{ role: 'resetZoom', label: 'Actual Size' },
|
|
328
|
+
{ role: 'zoomIn', label: 'Zoom In' },
|
|
329
|
+
{ role: 'zoomOut', label: 'Zoom Out' },
|
|
330
|
+
{ type: 'separator' },
|
|
331
|
+
{ role: 'togglefullscreen', label: 'Toggle Full Screen' },
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
label: 'Window',
|
|
336
|
+
submenu: [
|
|
337
|
+
{ role: 'minimize', label: 'Minimize' },
|
|
338
|
+
{ role: 'close', label: 'Close' },
|
|
339
|
+
],
|
|
340
|
+
},
|
|
341
|
+
];
|
|
342
|
+
// macOS specific menu adjustments
|
|
343
|
+
if (process.platform === 'darwin') {
|
|
344
|
+
template[0].label = 'PROJAX UI';
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
// Windows/Linux: Remove the app menu and move items to File menu
|
|
348
|
+
template.shift();
|
|
349
|
+
const fileMenu = template[0];
|
|
350
|
+
if (fileMenu.submenu && Array.isArray(fileMenu.submenu)) {
|
|
351
|
+
fileMenu.submenu.push({ type: 'separator' }, { role: 'quit', label: 'Exit' });
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const menu = electron_1.Menu.buildFromTemplate(template);
|
|
355
|
+
electron_1.Menu.setApplicationMenu(menu);
|
|
356
|
+
}
|
|
158
357
|
electron_1.app.whenReady().then(() => {
|
|
358
|
+
createMenu();
|
|
159
359
|
startAPIServer();
|
|
160
360
|
createWindow();
|
|
161
361
|
electron_1.app.on('activate', () => {
|
|
@@ -195,9 +395,42 @@ electron_1.ipcMain.handle('get-app-version', async () => {
|
|
|
195
395
|
electron_1.ipcMain.handle('get-projects', async () => {
|
|
196
396
|
try {
|
|
197
397
|
console.log('Getting projects from database...');
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
398
|
+
// Wait a bit for API server to be ready if it just started
|
|
399
|
+
// Try to get projects, with a retry if it fails
|
|
400
|
+
let projects = [];
|
|
401
|
+
let lastError = null;
|
|
402
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
403
|
+
try {
|
|
404
|
+
projects = (0, core_1.getAllProjects)();
|
|
405
|
+
console.log(`Found ${projects.length} project(s)`);
|
|
406
|
+
return projects;
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
410
|
+
if (attempt < 2) {
|
|
411
|
+
// Wait a bit before retrying (API server might be starting)
|
|
412
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// If all retries failed, try reading directly from database file as fallback
|
|
417
|
+
try {
|
|
418
|
+
const dataDir = path.join(os.homedir(), '.projax');
|
|
419
|
+
const dbPath = path.join(dataDir, 'data.json');
|
|
420
|
+
if (fs.existsSync(dbPath)) {
|
|
421
|
+
const dbContent = fs.readFileSync(dbPath, 'utf-8');
|
|
422
|
+
const db = JSON.parse(dbContent);
|
|
423
|
+
if (db.projects && Array.isArray(db.projects)) {
|
|
424
|
+
console.log(`Fallback: Found ${db.projects.length} project(s) from database file`);
|
|
425
|
+
return db.projects;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
catch (fileError) {
|
|
430
|
+
console.warn('Could not read projects from database file:', fileError);
|
|
431
|
+
}
|
|
432
|
+
// If we still don't have projects, throw the original error
|
|
433
|
+
throw lastError || new Error('Failed to get projects');
|
|
201
434
|
}
|
|
202
435
|
catch (error) {
|
|
203
436
|
console.error('Error getting projects:', error);
|
|
@@ -292,7 +525,7 @@ electron_1.ipcMain.handle('rename-project', async (_, projectId, newName) => {
|
|
|
292
525
|
return db.updateProjectName(projectId, newName);
|
|
293
526
|
});
|
|
294
527
|
// Get project scripts
|
|
295
|
-
electron_1.ipcMain.handle('get-project-scripts', async (_, projectPath) => {
|
|
528
|
+
electron_1.ipcMain.handle('get-project-scripts', async (_, projectPath, projectId) => {
|
|
296
529
|
// Try bundled path first (when bundled in CLI: dist/electron/main.js -> dist/script-runner.js)
|
|
297
530
|
// Then try local dev path (packages/desktop/dist/main.js -> packages/cli/dist/script-runner.js)
|
|
298
531
|
const bundledScriptRunnerPath = path.join(__dirname, '..', 'script-runner.js');
|
|
@@ -305,7 +538,69 @@ electron_1.ipcMain.handle('get-project-scripts', async (_, projectPath) => {
|
|
|
305
538
|
scriptRunnerPath = localScriptRunnerPath;
|
|
306
539
|
}
|
|
307
540
|
const { getProjectScripts } = await Promise.resolve(`${scriptRunnerPath}`).then(s => __importStar(require(s)));
|
|
308
|
-
|
|
541
|
+
// Get project settings if projectId is provided
|
|
542
|
+
// Read directly from database file instead of making HTTP requests
|
|
543
|
+
let scriptsPath = null;
|
|
544
|
+
if (projectId) {
|
|
545
|
+
try {
|
|
546
|
+
const dataDir = path.join(os.homedir(), '.projax');
|
|
547
|
+
const dbPath = path.join(dataDir, 'data.json');
|
|
548
|
+
// Try reading the file with a small retry in case it's being written
|
|
549
|
+
let db = null;
|
|
550
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
551
|
+
try {
|
|
552
|
+
if (fs.existsSync(dbPath)) {
|
|
553
|
+
const dbContent = fs.readFileSync(dbPath, 'utf-8');
|
|
554
|
+
db = JSON.parse(dbContent);
|
|
555
|
+
break; // Successfully read, exit retry loop
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
catch (readError) {
|
|
559
|
+
if (attempt < 2) {
|
|
560
|
+
// Wait a bit before retrying (only on first two attempts)
|
|
561
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
throw readError;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
if (db && db.project_settings && Array.isArray(db.project_settings)) {
|
|
568
|
+
const settings = db.project_settings.find((ps) => ps.project_id === projectId);
|
|
569
|
+
if (settings && settings.scripts_path) {
|
|
570
|
+
scriptsPath = settings.scripts_path;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
catch (error) {
|
|
575
|
+
// Silently fail - will use project root if settings can't be loaded
|
|
576
|
+
console.debug('Could not load project settings for scripts, using project root:', error);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// Use custom scripts path if set, otherwise use project root
|
|
580
|
+
let actualScriptsPath = projectPath;
|
|
581
|
+
if (scriptsPath) {
|
|
582
|
+
const joinedPath = path.join(projectPath, scriptsPath);
|
|
583
|
+
// Validate that the path exists
|
|
584
|
+
if (fs.existsSync(joinedPath)) {
|
|
585
|
+
const stats = fs.statSync(joinedPath);
|
|
586
|
+
if (stats.isDirectory()) {
|
|
587
|
+
// If it's a directory, use it directly
|
|
588
|
+
actualScriptsPath = joinedPath;
|
|
589
|
+
}
|
|
590
|
+
else if (stats.isFile()) {
|
|
591
|
+
// If it's a file, use the directory containing the file
|
|
592
|
+
actualScriptsPath = path.dirname(joinedPath);
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
console.warn(`Custom scripts path is not a directory or file: ${joinedPath}, using project root instead`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
console.warn(`Custom scripts path does not exist: ${joinedPath}, using project root instead`);
|
|
600
|
+
// Fall back to project root if custom path doesn't exist
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
const result = getProjectScripts(actualScriptsPath);
|
|
309
604
|
// Convert Map to array for IPC serialization
|
|
310
605
|
const scriptsArray = Array.from(result.scripts.entries()).map(([name, script]) => ({
|
|
311
606
|
name,
|
|
@@ -537,11 +832,11 @@ electron_1.ipcMain.handle('open-url', async (_, url) => {
|
|
|
537
832
|
}).unref();
|
|
538
833
|
});
|
|
539
834
|
// Open project in editor
|
|
540
|
-
electron_1.ipcMain.handle('open-in-editor', async (_, projectPath) => {
|
|
835
|
+
electron_1.ipcMain.handle('open-in-editor', async (_, projectPath, projectId) => {
|
|
541
836
|
// Try bundled path first (when bundled in CLI: dist/electron/main.js -> dist/core/settings)
|
|
542
837
|
// Then try local dev path (packages/desktop/dist/main.js -> packages/core/dist/settings)
|
|
543
|
-
const bundledSettingsPath = path.join(__dirname, '
|
|
544
|
-
const localSettingsPath = path.join(__dirname, '..', '..', '
|
|
838
|
+
const bundledSettingsPath = path.join(__dirname, 'core', 'settings');
|
|
839
|
+
const localSettingsPath = path.join(__dirname, '..', '..', 'core', 'dist', 'settings');
|
|
545
840
|
let settingsPath;
|
|
546
841
|
if (fs.existsSync(bundledSettingsPath + '.js')) {
|
|
547
842
|
settingsPath = bundledSettingsPath;
|
|
@@ -550,7 +845,41 @@ electron_1.ipcMain.handle('open-in-editor', async (_, projectPath) => {
|
|
|
550
845
|
settingsPath = localSettingsPath;
|
|
551
846
|
}
|
|
552
847
|
const { getEditorSettings } = require(settingsPath);
|
|
553
|
-
const
|
|
848
|
+
const globalEditorSettings = getEditorSettings();
|
|
849
|
+
// Check for project-specific editor settings
|
|
850
|
+
let editorSettings = globalEditorSettings;
|
|
851
|
+
if (projectId) {
|
|
852
|
+
try {
|
|
853
|
+
const ports = [38124, 38125, 38126, 38127, 38128, 3001];
|
|
854
|
+
let apiBaseUrl = '';
|
|
855
|
+
for (const port of ports) {
|
|
856
|
+
try {
|
|
857
|
+
const response = await fetch(`http://localhost:${port}/health`, { signal: AbortSignal.timeout(500) });
|
|
858
|
+
if (response.ok) {
|
|
859
|
+
apiBaseUrl = `http://localhost:${port}/api`;
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
catch {
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (apiBaseUrl) {
|
|
868
|
+
const settingsResponse = await fetch(`${apiBaseUrl}/projects/${projectId}/settings`);
|
|
869
|
+
if (settingsResponse.ok) {
|
|
870
|
+
const projectSettings = await settingsResponse.json();
|
|
871
|
+
if (projectSettings.editor && projectSettings.editor.type) {
|
|
872
|
+
// Use project-specific editor
|
|
873
|
+
editorSettings = projectSettings.editor;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
catch (error) {
|
|
879
|
+
console.error('Error loading project editor settings:', error);
|
|
880
|
+
// Fall back to global settings
|
|
881
|
+
}
|
|
882
|
+
}
|
|
554
883
|
let command;
|
|
555
884
|
let args = [projectPath];
|
|
556
885
|
if (editorSettings.type === 'custom' && editorSettings.customPath) {
|
|
@@ -580,6 +909,96 @@ electron_1.ipcMain.handle('open-in-editor', async (_, projectPath) => {
|
|
|
580
909
|
stdio: 'ignore',
|
|
581
910
|
}).unref();
|
|
582
911
|
});
|
|
912
|
+
// Open workspace in editor
|
|
913
|
+
electron_1.ipcMain.handle('open-workspace', async (_, workspaceId) => {
|
|
914
|
+
try {
|
|
915
|
+
// Get workspace file path from API (ensures file exists)
|
|
916
|
+
const ports = [38124, 38125, 38126, 38127, 38128, 3001];
|
|
917
|
+
let apiBaseUrl = '';
|
|
918
|
+
for (const port of ports) {
|
|
919
|
+
try {
|
|
920
|
+
const response = await fetch(`http://localhost:${port}/health`, { signal: AbortSignal.timeout(500) });
|
|
921
|
+
if (response.ok) {
|
|
922
|
+
apiBaseUrl = `http://localhost:${port}/api`;
|
|
923
|
+
break;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
catch {
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
if (!apiBaseUrl) {
|
|
931
|
+
throw new Error('API server not found');
|
|
932
|
+
}
|
|
933
|
+
// Get workspace file path (this will generate it if needed)
|
|
934
|
+
const response = await fetch(`${apiBaseUrl}/workspaces/${workspaceId}/file-path`);
|
|
935
|
+
if (!response.ok) {
|
|
936
|
+
throw new Error('Failed to get workspace file path');
|
|
937
|
+
}
|
|
938
|
+
const data = await response.json();
|
|
939
|
+
const workspace_file_path = data.workspace_file_path;
|
|
940
|
+
if (!fs.existsSync(workspace_file_path)) {
|
|
941
|
+
throw new Error('Workspace file does not exist');
|
|
942
|
+
}
|
|
943
|
+
// Open workspace file in editor (workspace files are opened with the workspace flag)
|
|
944
|
+
const bundledSettingsPath = path.join(__dirname, 'core', 'settings');
|
|
945
|
+
const localSettingsPath = path.join(__dirname, '..', '..', 'core', 'dist', 'settings');
|
|
946
|
+
let settingsPath;
|
|
947
|
+
if (fs.existsSync(bundledSettingsPath + '.js')) {
|
|
948
|
+
settingsPath = bundledSettingsPath;
|
|
949
|
+
}
|
|
950
|
+
else {
|
|
951
|
+
settingsPath = localSettingsPath;
|
|
952
|
+
}
|
|
953
|
+
const { getEditorSettings } = require(settingsPath);
|
|
954
|
+
const editorSettings = getEditorSettings();
|
|
955
|
+
let command;
|
|
956
|
+
let args = [];
|
|
957
|
+
if (editorSettings.type === 'custom' && editorSettings.customPath) {
|
|
958
|
+
command = editorSettings.customPath;
|
|
959
|
+
// For custom editors, try workspace file as argument
|
|
960
|
+
args = [workspace_file_path];
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
switch (editorSettings.type) {
|
|
964
|
+
case 'vscode':
|
|
965
|
+
case 'cursor':
|
|
966
|
+
case 'windsurf':
|
|
967
|
+
// VS Code, Cursor, and Windsurf support opening workspace files directly
|
|
968
|
+
command = editorSettings.type === 'vscode' ? 'code' : editorSettings.type === 'cursor' ? 'cursor' : 'windsurf';
|
|
969
|
+
args = [workspace_file_path];
|
|
970
|
+
break;
|
|
971
|
+
case 'zed':
|
|
972
|
+
// Zed doesn't support workspace files, open the first project folder instead
|
|
973
|
+
const workspaceContent = JSON.parse(fs.readFileSync(workspace_file_path, 'utf-8'));
|
|
974
|
+
if (workspaceContent.folders && workspaceContent.folders.length > 0) {
|
|
975
|
+
const firstFolder = workspaceContent.folders[0];
|
|
976
|
+
const folderPath = path.isAbsolute(firstFolder.path)
|
|
977
|
+
? firstFolder.path
|
|
978
|
+
: path.resolve(path.dirname(workspace_file_path), firstFolder.path);
|
|
979
|
+
command = 'zed';
|
|
980
|
+
args = [folderPath];
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
throw new Error('Workspace has no folders to open');
|
|
984
|
+
}
|
|
985
|
+
break;
|
|
986
|
+
default:
|
|
987
|
+
command = 'code';
|
|
988
|
+
args = [workspace_file_path];
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
const { spawn } = require('child_process');
|
|
992
|
+
spawn(command, args, {
|
|
993
|
+
detached: true,
|
|
994
|
+
stdio: 'ignore',
|
|
995
|
+
}).unref();
|
|
996
|
+
}
|
|
997
|
+
catch (error) {
|
|
998
|
+
console.error('Error opening workspace:', error);
|
|
999
|
+
throw error;
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
583
1002
|
// Open project directory in file manager
|
|
584
1003
|
electron_1.ipcMain.handle('open-in-files', async (_, projectPath) => {
|
|
585
1004
|
try {
|
|
@@ -590,12 +1009,23 @@ electron_1.ipcMain.handle('open-in-files', async (_, projectPath) => {
|
|
|
590
1009
|
throw error;
|
|
591
1010
|
}
|
|
592
1011
|
});
|
|
1012
|
+
// Open file path in file manager (reveals file in finder/explorer)
|
|
1013
|
+
electron_1.ipcMain.handle('open-file-path', async (_, filePath) => {
|
|
1014
|
+
try {
|
|
1015
|
+
// Use shell.showItemInFolder to reveal the file in the file manager
|
|
1016
|
+
electron_1.shell.showItemInFolder(filePath);
|
|
1017
|
+
}
|
|
1018
|
+
catch (error) {
|
|
1019
|
+
console.error('Error opening file path:', error);
|
|
1020
|
+
throw error;
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
593
1023
|
// Get settings
|
|
594
1024
|
electron_1.ipcMain.handle('get-settings', async () => {
|
|
595
1025
|
try {
|
|
596
1026
|
// Load settings module directly
|
|
597
|
-
const bundledSettingsPath = path.join(__dirname, '
|
|
598
|
-
const localSettingsPath = path.join(__dirname, '..', '..', '
|
|
1027
|
+
const bundledSettingsPath = path.join(__dirname, 'core', 'settings');
|
|
1028
|
+
const localSettingsPath = path.join(__dirname, '..', '..', 'core', 'dist', 'settings');
|
|
599
1029
|
let settingsPath;
|
|
600
1030
|
if (fs.existsSync(bundledSettingsPath + '.js')) {
|
|
601
1031
|
settingsPath = bundledSettingsPath;
|
|
@@ -615,8 +1045,8 @@ electron_1.ipcMain.handle('get-settings', async () => {
|
|
|
615
1045
|
electron_1.ipcMain.handle('save-settings', async (_, settings) => {
|
|
616
1046
|
try {
|
|
617
1047
|
// Load settings module directly
|
|
618
|
-
const bundledSettingsPath = path.join(__dirname, '
|
|
619
|
-
const localSettingsPath = path.join(__dirname, '..', '..', '
|
|
1048
|
+
const bundledSettingsPath = path.join(__dirname, 'core', 'settings');
|
|
1049
|
+
const localSettingsPath = path.join(__dirname, '..', '..', 'core', 'dist', 'settings');
|
|
620
1050
|
let settingsPath;
|
|
621
1051
|
if (fs.existsSync(bundledSettingsPath + '.js')) {
|
|
622
1052
|
settingsPath = bundledSettingsPath;
|
|
@@ -722,3 +1152,203 @@ electron_1.ipcMain.handle('unwatch-process-output', async (_, pid) => {
|
|
|
722
1152
|
}
|
|
723
1153
|
return { success: false };
|
|
724
1154
|
});
|
|
1155
|
+
// Workspace handlers
|
|
1156
|
+
electron_1.ipcMain.handle('get-workspaces', async () => {
|
|
1157
|
+
try {
|
|
1158
|
+
// Try to find the correct API port
|
|
1159
|
+
const ports = [38124, 38125, 38126, 38127, 38128, 3001];
|
|
1160
|
+
let apiBaseUrl = '';
|
|
1161
|
+
for (const port of ports) {
|
|
1162
|
+
try {
|
|
1163
|
+
const response = await fetch(`http://localhost:${port}/health`, { signal: AbortSignal.timeout(500) });
|
|
1164
|
+
if (response.ok) {
|
|
1165
|
+
apiBaseUrl = `http://localhost:${port}/api`;
|
|
1166
|
+
break;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
catch {
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
// Try fetching from API with retries
|
|
1174
|
+
if (apiBaseUrl) {
|
|
1175
|
+
for (let i = 0; i < 3; i++) {
|
|
1176
|
+
try {
|
|
1177
|
+
const response = await fetch(`${apiBaseUrl}/workspaces`);
|
|
1178
|
+
if (response.ok) {
|
|
1179
|
+
const workspaces = await response.json();
|
|
1180
|
+
console.log(`[main] Fetched ${workspaces.length} workspace(s) from API.`);
|
|
1181
|
+
return workspaces;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
catch (error) {
|
|
1185
|
+
console.debug(`[main] API fetch attempt ${i + 1} failed:`, error);
|
|
1186
|
+
if (i < 2) {
|
|
1187
|
+
await new Promise(resolve => setTimeout(resolve, 500)); // Wait 500ms before retry
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
// Fallback to reading directly from database file if API is not available or failed
|
|
1193
|
+
try {
|
|
1194
|
+
console.warn('[main] API not available or failed after retries. Reading workspaces directly from database file.');
|
|
1195
|
+
const dataDir = path.join(os.homedir(), '.projax');
|
|
1196
|
+
const dbPath = path.join(dataDir, 'data.json');
|
|
1197
|
+
if (fs.existsSync(dbPath)) {
|
|
1198
|
+
const dbContent = fs.readFileSync(dbPath, 'utf-8');
|
|
1199
|
+
const db = JSON.parse(dbContent);
|
|
1200
|
+
const workspaces = db.workspaces || [];
|
|
1201
|
+
console.log(`[main] Found ${workspaces.length} workspace(s) in database file.`);
|
|
1202
|
+
return workspaces;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
catch (fileError) {
|
|
1206
|
+
console.warn('[main] Could not read workspaces from database file:', fileError);
|
|
1207
|
+
}
|
|
1208
|
+
// If we still don't have workspaces, return empty array
|
|
1209
|
+
console.warn('[main] No workspaces found, returning empty array.');
|
|
1210
|
+
return [];
|
|
1211
|
+
}
|
|
1212
|
+
catch (error) {
|
|
1213
|
+
console.error('Error getting workspaces:', error);
|
|
1214
|
+
// Return empty array instead of throwing to prevent app crash
|
|
1215
|
+
return [];
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
1218
|
+
electron_1.ipcMain.handle('add-workspace', async (_, workspace) => {
|
|
1219
|
+
try {
|
|
1220
|
+
// Try to find the correct API port
|
|
1221
|
+
const ports = [38124, 38125, 38126, 38127, 38128, 3001];
|
|
1222
|
+
let apiBaseUrl = '';
|
|
1223
|
+
for (const port of ports) {
|
|
1224
|
+
try {
|
|
1225
|
+
const response = await fetch(`http://localhost:${port}/health`, { signal: AbortSignal.timeout(500) });
|
|
1226
|
+
if (response.ok) {
|
|
1227
|
+
apiBaseUrl = `http://localhost:${port}/api`;
|
|
1228
|
+
break;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
catch {
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
if (!apiBaseUrl) {
|
|
1236
|
+
throw new Error('API server not found');
|
|
1237
|
+
}
|
|
1238
|
+
const response = await fetch(`${apiBaseUrl}/workspaces`, {
|
|
1239
|
+
method: 'POST',
|
|
1240
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1241
|
+
body: JSON.stringify(workspace),
|
|
1242
|
+
});
|
|
1243
|
+
if (!response.ok) {
|
|
1244
|
+
const errorData = await response.json();
|
|
1245
|
+
throw new Error(errorData.error || 'Failed to add workspace');
|
|
1246
|
+
}
|
|
1247
|
+
return await response.json();
|
|
1248
|
+
}
|
|
1249
|
+
catch (error) {
|
|
1250
|
+
console.error('Error adding workspace:', error);
|
|
1251
|
+
throw error;
|
|
1252
|
+
}
|
|
1253
|
+
});
|
|
1254
|
+
electron_1.ipcMain.handle('remove-workspace', async (_, workspaceId) => {
|
|
1255
|
+
try {
|
|
1256
|
+
const response = await fetch(`http://localhost:3001/api/workspaces/${workspaceId}`, {
|
|
1257
|
+
method: 'DELETE',
|
|
1258
|
+
});
|
|
1259
|
+
if (!response.ok)
|
|
1260
|
+
throw new Error('Failed to remove workspace');
|
|
1261
|
+
}
|
|
1262
|
+
catch (error) {
|
|
1263
|
+
console.error('Error removing workspace:', error);
|
|
1264
|
+
throw error;
|
|
1265
|
+
}
|
|
1266
|
+
});
|
|
1267
|
+
// Backup handlers
|
|
1268
|
+
electron_1.ipcMain.handle('create-backup', async (_, outputPath) => {
|
|
1269
|
+
try {
|
|
1270
|
+
// Try to load from dist first, then src
|
|
1271
|
+
const backupUtilsPath = path.join(__dirname, '..', '..', 'core', 'dist', 'backup-utils.js');
|
|
1272
|
+
const backupUtilsSrcPath = path.join(__dirname, '..', '..', 'core', 'src', 'backup-utils.ts');
|
|
1273
|
+
let backupUtils;
|
|
1274
|
+
if (fs.existsSync(backupUtilsPath)) {
|
|
1275
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1276
|
+
backupUtils = require(backupUtilsPath);
|
|
1277
|
+
}
|
|
1278
|
+
else if (fs.existsSync(backupUtilsSrcPath.replace('.ts', '.js'))) {
|
|
1279
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1280
|
+
backupUtils = require(backupUtilsSrcPath.replace('.ts', '.js'));
|
|
1281
|
+
}
|
|
1282
|
+
else {
|
|
1283
|
+
throw new Error('Backup utils not found');
|
|
1284
|
+
}
|
|
1285
|
+
const { createBackup } = backupUtils;
|
|
1286
|
+
const backupPath = await createBackup(outputPath);
|
|
1287
|
+
return { success: true, backup_path: backupPath };
|
|
1288
|
+
}
|
|
1289
|
+
catch (error) {
|
|
1290
|
+
console.error('Error creating backup:', error);
|
|
1291
|
+
throw error;
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
electron_1.ipcMain.handle('restore-backup', async (_, backupPath) => {
|
|
1295
|
+
try {
|
|
1296
|
+
// Try to load from dist first, then src
|
|
1297
|
+
const backupUtilsPath = path.join(__dirname, '..', '..', 'core', 'dist', 'backup-utils.js');
|
|
1298
|
+
const backupUtilsSrcPath = path.join(__dirname, '..', '..', 'core', 'src', 'backup-utils.ts');
|
|
1299
|
+
let backupUtils;
|
|
1300
|
+
if (fs.existsSync(backupUtilsPath)) {
|
|
1301
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1302
|
+
backupUtils = require(backupUtilsPath);
|
|
1303
|
+
}
|
|
1304
|
+
else if (fs.existsSync(backupUtilsSrcPath.replace('.ts', '.js'))) {
|
|
1305
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1306
|
+
backupUtils = require(backupUtilsSrcPath.replace('.ts', '.js'));
|
|
1307
|
+
}
|
|
1308
|
+
else {
|
|
1309
|
+
throw new Error('Backup utils not found');
|
|
1310
|
+
}
|
|
1311
|
+
const { restoreBackup } = backupUtils;
|
|
1312
|
+
await restoreBackup(backupPath);
|
|
1313
|
+
return { success: true };
|
|
1314
|
+
}
|
|
1315
|
+
catch (error) {
|
|
1316
|
+
console.error('Error restoring backup:', error);
|
|
1317
|
+
throw error;
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
// File dialog handlers
|
|
1321
|
+
electron_1.ipcMain.handle('show-save-dialog', async (_, options) => {
|
|
1322
|
+
if (!mainWindow)
|
|
1323
|
+
return { canceled: true };
|
|
1324
|
+
const result = await electron_1.dialog.showSaveDialog(mainWindow, {
|
|
1325
|
+
title: options.title || 'Save File',
|
|
1326
|
+
defaultPath: options.defaultPath,
|
|
1327
|
+
filters: options.filters || [{ name: 'All Files', extensions: ['*'] }],
|
|
1328
|
+
});
|
|
1329
|
+
return result;
|
|
1330
|
+
});
|
|
1331
|
+
electron_1.ipcMain.handle('show-open-dialog', async (_, options) => {
|
|
1332
|
+
if (!mainWindow)
|
|
1333
|
+
return { canceled: true };
|
|
1334
|
+
const result = await electron_1.dialog.showOpenDialog(mainWindow, {
|
|
1335
|
+
title: options.title || 'Open File',
|
|
1336
|
+
defaultPath: options.defaultPath,
|
|
1337
|
+
filters: options.filters || [{ name: 'All Files', extensions: ['*'] }],
|
|
1338
|
+
properties: options.properties || ['openFile'],
|
|
1339
|
+
});
|
|
1340
|
+
return result;
|
|
1341
|
+
});
|
|
1342
|
+
electron_1.ipcMain.handle('select-file', async (_, options) => {
|
|
1343
|
+
if (!mainWindow)
|
|
1344
|
+
return null;
|
|
1345
|
+
const result = await electron_1.dialog.showOpenDialog(mainWindow, {
|
|
1346
|
+
title: options.title || 'Select File',
|
|
1347
|
+
defaultPath: options.defaultPath,
|
|
1348
|
+
filters: options.filters || [{ name: 'All Files', extensions: ['*'] }],
|
|
1349
|
+
properties: ['openFile'],
|
|
1350
|
+
});
|
|
1351
|
+
if (result.canceled || result.filePaths.length === 0)
|
|
1352
|
+
return null;
|
|
1353
|
+
return result.filePaths[0];
|
|
1354
|
+
});
|