@gnsx/genesys.sdk 4.2.9

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 (185) hide show
  1. package/README.md +60 -0
  2. package/dist/src/asset-pack/eslint.config.js +58 -0
  3. package/dist/src/asset-pack/scripts/post-install.js +64 -0
  4. package/dist/src/asset-pack/src/index.js +1 -0
  5. package/dist/src/core/cli.js +303 -0
  6. package/dist/src/core/common.js +325 -0
  7. package/dist/src/core/index.js +6 -0
  8. package/dist/src/core/tools/build-project.js +456 -0
  9. package/dist/src/core/tools/index.js +2 -0
  10. package/dist/src/core/tools/new-asset-pack.js +153 -0
  11. package/dist/src/core/tools/new-project.js +293 -0
  12. package/dist/src/core/types.js +1 -0
  13. package/dist/src/dependencies.js +84 -0
  14. package/dist/src/electron/IpcSerializableError.js +38 -0
  15. package/dist/src/electron/api.js +7 -0
  16. package/dist/src/electron/backend/actions.js +56 -0
  17. package/dist/src/electron/backend/handler.js +452 -0
  18. package/dist/src/electron/backend/logging.js +41 -0
  19. package/dist/src/electron/backend/main.js +369 -0
  20. package/dist/src/electron/backend/menu.js +196 -0
  21. package/dist/src/electron/backend/state.js +201 -0
  22. package/dist/src/electron/backend/telemetry.js +9 -0
  23. package/dist/src/electron/backend/tools/const.js +9 -0
  24. package/dist/src/electron/backend/tools/file-server.js +383 -0
  25. package/dist/src/electron/backend/tools/open-project.js +249 -0
  26. package/dist/src/electron/backend/window.js +161 -0
  27. package/dist/src/templates/eslint.config.js +58 -0
  28. package/dist/src/templates/scripts/genesys/build-project.js +42 -0
  29. package/dist/src/templates/scripts/genesys/calc-bounding-box.js +205 -0
  30. package/dist/src/templates/scripts/genesys/common.js +36 -0
  31. package/dist/src/templates/scripts/genesys/const.js +9 -0
  32. package/dist/src/templates/scripts/genesys/dev/dump-default-scene.js +8 -0
  33. package/dist/src/templates/scripts/genesys/dev/generate-manifest.js +116 -0
  34. package/dist/src/templates/scripts/genesys/dev/launcher.js +39 -0
  35. package/dist/src/templates/scripts/genesys/dev/storage-provider.js +188 -0
  36. package/dist/src/templates/scripts/genesys/dev/update-template-scenes.js +67 -0
  37. package/dist/src/templates/scripts/genesys/doc-server.js +12 -0
  38. package/dist/src/templates/scripts/genesys/genesys-mcp.js +413 -0
  39. package/dist/src/templates/scripts/genesys/mcp/doc-tools.js +70 -0
  40. package/dist/src/templates/scripts/genesys/mcp/editor-functions.js +123 -0
  41. package/dist/src/templates/scripts/genesys/mcp/editor-tools.js +51 -0
  42. package/dist/src/templates/scripts/genesys/mcp/get-scene-state.js +26 -0
  43. package/dist/src/templates/scripts/genesys/mcp/run-subprocess.js +23 -0
  44. package/dist/src/templates/scripts/genesys/mcp/search-actors.js +703 -0
  45. package/dist/src/templates/scripts/genesys/mcp/search-assets.js +296 -0
  46. package/dist/src/templates/scripts/genesys/mcp/utils.js +234 -0
  47. package/dist/src/templates/scripts/genesys/migrate-scenes-and-prefabs.js +252 -0
  48. package/dist/src/templates/scripts/genesys/misc.js +32 -0
  49. package/dist/src/templates/scripts/genesys/mock.js +5 -0
  50. package/dist/src/templates/scripts/genesys/place-actors.js +112 -0
  51. package/dist/src/templates/scripts/genesys/post-install.js +33 -0
  52. package/dist/src/templates/scripts/genesys/remove-engine-comments.js +113 -0
  53. package/dist/src/templates/scripts/genesys/storageProvider.js +146 -0
  54. package/dist/src/templates/scripts/genesys/validate-prefabs.js +115 -0
  55. package/dist/src/templates/src/index.js +20 -0
  56. package/dist/src/templates/src/templates/firstPerson/src/auto-imports.js +1 -0
  57. package/dist/src/templates/src/templates/firstPerson/src/game.js +30 -0
  58. package/dist/src/templates/src/templates/firstPerson/src/player.js +55 -0
  59. package/dist/src/templates/src/templates/fps/src/auto-imports.js +1 -0
  60. package/dist/src/templates/src/templates/fps/src/game.js +30 -0
  61. package/dist/src/templates/src/templates/fps/src/player.js +60 -0
  62. package/dist/src/templates/src/templates/fps/src/weapon.js +54 -0
  63. package/dist/src/templates/src/templates/freeCamera/src/auto-imports.js +1 -0
  64. package/dist/src/templates/src/templates/freeCamera/src/game.js +30 -0
  65. package/dist/src/templates/src/templates/freeCamera/src/player.js +38 -0
  66. package/dist/src/templates/src/templates/sideScroller/src/auto-imports.js +1 -0
  67. package/dist/src/templates/src/templates/sideScroller/src/const.js +43 -0
  68. package/dist/src/templates/src/templates/sideScroller/src/game.js +102 -0
  69. package/dist/src/templates/src/templates/sideScroller/src/level-generator.js +249 -0
  70. package/dist/src/templates/src/templates/sideScroller/src/player.js +100 -0
  71. package/dist/src/templates/src/templates/thirdPerson/src/auto-imports.js +1 -0
  72. package/dist/src/templates/src/templates/thirdPerson/src/game.js +30 -0
  73. package/dist/src/templates/src/templates/thirdPerson/src/player.js +58 -0
  74. package/dist/src/templates/src/templates/vehicle/src/auto-imports.js +1 -0
  75. package/dist/src/templates/src/templates/vehicle/src/base-vehicle.js +122 -0
  76. package/dist/src/templates/src/templates/vehicle/src/game.js +33 -0
  77. package/dist/src/templates/src/templates/vehicle/src/mesh-vehicle.js +188 -0
  78. package/dist/src/templates/src/templates/vehicle/src/player.js +97 -0
  79. package/dist/src/templates/src/templates/vehicle/src/primitive-vehicle.js +258 -0
  80. package/dist/src/templates/src/templates/vehicle/src/ui-hints.js +100 -0
  81. package/dist/src/templates/src/templates/vr-game/src/auto-imports.js +1 -0
  82. package/dist/src/templates/src/templates/vr-game/src/game.js +55 -0
  83. package/dist/src/templates/src/templates/vr-game/src/sample-vr-actor.js +29 -0
  84. package/dist/src/templates/vite.config.js +46 -0
  85. package/package.json +181 -0
  86. package/scripts/post-install.ts +143 -0
  87. package/src/asset-pack/.gitattributes +89 -0
  88. package/src/asset-pack/.github/workflows/publish.yml +90 -0
  89. package/src/asset-pack/eslint.config.js +59 -0
  90. package/src/asset-pack/gitignore +11 -0
  91. package/src/asset-pack/scripts/post-install.ts +81 -0
  92. package/src/asset-pack/src/index.ts +0 -0
  93. package/src/asset-pack/tsconfig.json +34 -0
  94. package/src/templates/.cursor/mcp.json +20 -0
  95. package/src/templates/.cursorignore +2 -0
  96. package/src/templates/.gitattributes +89 -0
  97. package/src/templates/.vscode/settings.json +6 -0
  98. package/src/templates/AGENTS.md +104 -0
  99. package/src/templates/CLAUDE.md +1 -0
  100. package/src/templates/README.md +24 -0
  101. package/src/templates/eslint.config.js +60 -0
  102. package/src/templates/gitignore +11 -0
  103. package/src/templates/index.html +34 -0
  104. package/src/templates/pnpm-lock.yaml +3676 -0
  105. package/src/templates/scripts/genesys/build-project.ts +51 -0
  106. package/src/templates/scripts/genesys/calc-bounding-box.ts +272 -0
  107. package/src/templates/scripts/genesys/common.ts +46 -0
  108. package/src/templates/scripts/genesys/const.ts +9 -0
  109. package/src/templates/scripts/genesys/dev/dump-default-scene.ts +11 -0
  110. package/src/templates/scripts/genesys/dev/generate-manifest.ts +146 -0
  111. package/src/templates/scripts/genesys/dev/launcher.ts +46 -0
  112. package/src/templates/scripts/genesys/dev/storage-provider.ts +229 -0
  113. package/src/templates/scripts/genesys/dev/update-template-scenes.ts +84 -0
  114. package/src/templates/scripts/genesys/doc-server.ts +16 -0
  115. package/src/templates/scripts/genesys/genesys-mcp.ts +526 -0
  116. package/src/templates/scripts/genesys/mcp/doc-tools.ts +86 -0
  117. package/src/templates/scripts/genesys/mcp/editor-functions.ts +151 -0
  118. package/src/templates/scripts/genesys/mcp/editor-tools.ts +73 -0
  119. package/src/templates/scripts/genesys/mcp/get-scene-state.ts +35 -0
  120. package/src/templates/scripts/genesys/mcp/run-subprocess.ts +30 -0
  121. package/src/templates/scripts/genesys/mcp/search-actors.ts +858 -0
  122. package/src/templates/scripts/genesys/mcp/search-assets.ts +380 -0
  123. package/src/templates/scripts/genesys/mcp/utils.ts +281 -0
  124. package/src/templates/scripts/genesys/migrate-scenes-and-prefabs.ts +301 -0
  125. package/src/templates/scripts/genesys/misc.ts +42 -0
  126. package/src/templates/scripts/genesys/mock.ts +6 -0
  127. package/src/templates/scripts/genesys/place-actors.ts +179 -0
  128. package/src/templates/scripts/genesys/post-install.ts +39 -0
  129. package/src/templates/scripts/genesys/prefab.schema.json +85 -0
  130. package/src/templates/scripts/genesys/remove-engine-comments.ts +135 -0
  131. package/src/templates/scripts/genesys/run-mcp-inspector.bat +5 -0
  132. package/src/templates/scripts/genesys/storageProvider.ts +182 -0
  133. package/src/templates/scripts/genesys/validate-prefabs.ts +138 -0
  134. package/src/templates/src/index.ts +22 -0
  135. package/src/templates/src/templates/firstPerson/assets/default.genesys-scene +166 -0
  136. package/src/templates/src/templates/firstPerson/src/auto-imports.ts +0 -0
  137. package/src/templates/src/templates/firstPerson/src/game.ts +39 -0
  138. package/src/templates/src/templates/firstPerson/src/player.ts +59 -0
  139. package/src/templates/src/templates/fps/assets/default.genesys-scene +9460 -0
  140. package/src/templates/src/templates/fps/assets/models/SM_Beam_400.glb +0 -0
  141. package/src/templates/src/templates/fps/assets/models/SM_ChamferCube.glb +0 -0
  142. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400.glb +0 -0
  143. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400_Orange.glb +0 -0
  144. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400.glb +0 -0
  145. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400_Orange.glb +0 -0
  146. package/src/templates/src/templates/fps/assets/models/SM_Ramp_400x400.glb +0 -0
  147. package/src/templates/src/templates/fps/assets/models/SM_Rifle.glb +0 -0
  148. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200.glb +0 -0
  149. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200_Orange.glb +0 -0
  150. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400.glb +0 -0
  151. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400_Orange.glb +0 -0
  152. package/src/templates/src/templates/fps/src/auto-imports.ts +0 -0
  153. package/src/templates/src/templates/fps/src/game.ts +39 -0
  154. package/src/templates/src/templates/fps/src/player.ts +66 -0
  155. package/src/templates/src/templates/fps/src/weapon.ts +47 -0
  156. package/src/templates/src/templates/freeCamera/assets/default.genesys-scene +166 -0
  157. package/src/templates/src/templates/freeCamera/src/auto-imports.ts +0 -0
  158. package/src/templates/src/templates/freeCamera/src/game.ts +39 -0
  159. package/src/templates/src/templates/freeCamera/src/player.ts +40 -0
  160. package/src/templates/src/templates/sideScroller/assets/default.genesys-scene +122 -0
  161. package/src/templates/src/templates/sideScroller/src/auto-imports.ts +0 -0
  162. package/src/templates/src/templates/sideScroller/src/const.ts +46 -0
  163. package/src/templates/src/templates/sideScroller/src/game.ts +121 -0
  164. package/src/templates/src/templates/sideScroller/src/level-generator.ts +361 -0
  165. package/src/templates/src/templates/sideScroller/src/player.ts +123 -0
  166. package/src/templates/src/templates/thirdPerson/assets/default.genesys-scene +166 -0
  167. package/src/templates/src/templates/thirdPerson/src/auto-imports.ts +0 -0
  168. package/src/templates/src/templates/thirdPerson/src/game.ts +39 -0
  169. package/src/templates/src/templates/thirdPerson/src/player.ts +58 -0
  170. package/src/templates/src/templates/vehicle/assets/default.genesys-scene +226 -0
  171. package/src/templates/src/templates/vehicle/assets/models/cyberTruck/chassis.glb +0 -0
  172. package/src/templates/src/templates/vehicle/assets/models/cyberTruck/wheel.glb +0 -0
  173. package/src/templates/src/templates/vehicle/src/auto-imports.ts +0 -0
  174. package/src/templates/src/templates/vehicle/src/base-vehicle.ts +145 -0
  175. package/src/templates/src/templates/vehicle/src/game.ts +43 -0
  176. package/src/templates/src/templates/vehicle/src/mesh-vehicle.ts +189 -0
  177. package/src/templates/src/templates/vehicle/src/player.ts +106 -0
  178. package/src/templates/src/templates/vehicle/src/primitive-vehicle.ts +264 -0
  179. package/src/templates/src/templates/vehicle/src/ui-hints.ts +101 -0
  180. package/src/templates/src/templates/vr-game/assets/default.genesys-scene +247 -0
  181. package/src/templates/src/templates/vr-game/src/auto-imports.ts +1 -0
  182. package/src/templates/src/templates/vr-game/src/game.ts +66 -0
  183. package/src/templates/src/templates/vr-game/src/sample-vr-actor.ts +26 -0
  184. package/src/templates/tsconfig.json +35 -0
  185. package/src/templates/vite.config.ts +52 -0
@@ -0,0 +1,369 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { app, BrowserWindow, dialog, Menu, session } from 'electron';
5
+ import isDev from 'electron-is-dev';
6
+ import log from 'electron-log';
7
+ import electronUpdater from 'electron-updater';
8
+ import { getAppInfo } from './handler.js';
9
+ import { configureLogging, logger } from './logging.js';
10
+ import { buildAppMenu } from './menu.js';
11
+ import { configureTelemetry } from './telemetry.js';
12
+ import { GENESYS_LOCAL_URL, GENESYS_URL } from './tools/const.js';
13
+ import { createWindow, parseFeatures } from './window.js';
14
+ const { autoUpdater } = electronUpdater;
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = path.dirname(__filename);
17
+ export let mainWindow = null;
18
+ // Deep link URL storage
19
+ let pendingDeepLinkUrl = null;
20
+ export function getPendingDeepLinkUrl() {
21
+ return pendingDeepLinkUrl;
22
+ }
23
+ export function clearPendingDeepLinkUrl() {
24
+ pendingDeepLinkUrl = null;
25
+ }
26
+ configureTelemetry();
27
+ configureLogging();
28
+ start();
29
+ function start() {
30
+ // Register deep link protocols
31
+ registerDeepLinkProtocols();
32
+ const hasLock = app.requestSingleInstanceLock();
33
+ app.on('second-instance', (event, argv, workingDirectory, additionalData) => {
34
+ logger.log('🔗 [deep-link] Second instance detected, checking for deep link URL');
35
+ // Handle deep link from second instance
36
+ const deepLinkUrl = getDeepLinkFromArgs(argv);
37
+ if (deepLinkUrl) {
38
+ logger.log('🔗 [deep-link] Processing URL from second instance:', deepLinkUrl);
39
+ handleDeepLink(deepLinkUrl);
40
+ }
41
+ if (mainWindow) {
42
+ mainWindow.isMinimized() && mainWindow.restore();
43
+ mainWindow.focus();
44
+ }
45
+ });
46
+ if (!hasLock) {
47
+ return app.quit();
48
+ }
49
+ // Handle deep link from first instance
50
+ const deepLinkUrl = getDeepLinkFromArgs(process.argv);
51
+ if (deepLinkUrl) {
52
+ logger.log('🔗 [deep-link] Processing URL from first instance:', deepLinkUrl);
53
+ pendingDeepLinkUrl = deepLinkUrl;
54
+ }
55
+ // Get custom URL from command line arguments (--url=https://example.com)
56
+ const customUrl = app.commandLine.hasSwitch('url') ? app.commandLine.getSwitchValue('url') : undefined;
57
+ if (customUrl) {
58
+ logger.log('Custom URL provided:', customUrl);
59
+ }
60
+ logger.log('===============================================');
61
+ logger.log(`\n${JSON.stringify(getAppInfo(), null, 2)}`);
62
+ logger.log('===============================================');
63
+ // Handle macOS deep links
64
+ app.on('open-url', (event, url) => {
65
+ event.preventDefault();
66
+ logger.log('🔗 [deep-link] macOS open-url event:', url);
67
+ handleDeepLink(url);
68
+ });
69
+ app.whenReady().then(async () => {
70
+ await initMainWindow();
71
+ Menu.setApplicationMenu(buildAppMenu(mainWindow));
72
+ checkForUpdates();
73
+ // Process pending deep link after window is ready
74
+ if (pendingDeepLinkUrl) {
75
+ logger.log('🔗 [deep-link] Processing pending deep link URL:', pendingDeepLinkUrl);
76
+ handleDeepLink(pendingDeepLinkUrl);
77
+ pendingDeepLinkUrl = null;
78
+ }
79
+ });
80
+ app.on('window-all-closed', () => {
81
+ if (process.platform !== 'darwin')
82
+ app.quit();
83
+ });
84
+ }
85
+ /** Register deep link protocols based on environment */
86
+ function registerDeepLinkProtocols() {
87
+ const protocol = isDev ? 'genesys-dev' : 'genesys';
88
+ let success = false;
89
+ if (process.defaultApp) {
90
+ // Development mode - need to specify execPath and argv
91
+ if (process.argv.length >= 2) {
92
+ success = app.setAsDefaultProtocolClient(protocol, process.execPath, [path.resolve(process.argv[1])]);
93
+ }
94
+ }
95
+ else {
96
+ // Production mode - simple registration
97
+ success = app.setAsDefaultProtocolClient(protocol);
98
+ }
99
+ if (success) {
100
+ logger.log(`🔗 [deep-link] Successfully registered protocol: ${protocol}://`);
101
+ }
102
+ else {
103
+ logger.warn(`⚠️ [deep-link] Failed to register protocol: ${protocol}://`);
104
+ }
105
+ }
106
+ /** Extract deep link URL from command line arguments */
107
+ function getDeepLinkFromArgs(argv) {
108
+ const protocol = isDev ? 'genesys-dev://' : 'genesys://';
109
+ for (const arg of argv) {
110
+ if (arg.startsWith(protocol)) {
111
+ return arg;
112
+ }
113
+ }
114
+ return null;
115
+ }
116
+ /** Handle deep link URL */
117
+ function handleDeepLink(url) {
118
+ logger.log('🔗 [deep-link] Handling deep link:', url);
119
+ try {
120
+ const parsedUrl = new URL(url);
121
+ logger.log('🔗 [deep-link] Parsed URL:', parsedUrl.toString());
122
+ // Send deep link to renderer process if window is ready
123
+ if (mainWindow && !mainWindow.isDestroyed()) {
124
+ mainWindow.webContents.send('deep-link', parsedUrl.toString());
125
+ logger.log('🔗 [deep-link] Deep link sent to renderer process');
126
+ }
127
+ else {
128
+ // Store for later if window isn't ready
129
+ pendingDeepLinkUrl = url;
130
+ logger.log('🔗 [deep-link] Window not ready, storing deep link for later');
131
+ }
132
+ }
133
+ catch (error) {
134
+ logger.error('❌ [deep-link] Failed to parse deep link URL:', error);
135
+ }
136
+ }
137
+ const windowOptions = {
138
+ backgroundColor: '#13171b',
139
+ hasShadow: true,
140
+ titleBarStyle: 'hidden',
141
+ titleBarOverlay: false,
142
+ minWidth: 1024,
143
+ minHeight: 720,
144
+ };
145
+ async function showLocalDevError() {
146
+ const result = await dialog.showMessageBox(mainWindow, {
147
+ type: 'error',
148
+ buttons: ['Refresh (Ctrl+Shift+R)', 'OK'],
149
+ title: 'Local Development Server Not Running',
150
+ message: 'Failed to connect to local development server',
151
+ detail: 'Run "pnpm dev" on the .ai project and then refresh this page'
152
+ });
153
+ if (result.response === 0) {
154
+ // Refresh the page
155
+ try {
156
+ await mainWindow.loadURL(GENESYS_LOCAL_URL);
157
+ logger.log('Genesys.ai localhost');
158
+ }
159
+ catch (error) {
160
+ logger.error('Failed to load local URL:', error);
161
+ await showLocalDevError();
162
+ }
163
+ }
164
+ }
165
+ async function showUrlLoadError(message) {
166
+ await dialog.showMessageBox(mainWindow, {
167
+ type: 'error',
168
+ buttons: ['OK'],
169
+ title: 'Connection Error',
170
+ message,
171
+ detail: 'Please check your internet connection and try again'
172
+ });
173
+ }
174
+ async function initMainWindow() {
175
+ // Create main window with title bar overlay
176
+ mainWindow = createWindow('main', {
177
+ show: false,
178
+ title: 'Genesys',
179
+ ...windowOptions,
180
+ webPreferences: {
181
+ sandbox: true,
182
+ contextIsolation: true,
183
+ preload: path.join(__dirname, '../../../preload/electron/preload.js'),
184
+ nodeIntegration: false,
185
+ },
186
+ });
187
+ mainWindow.webContents.on('did-finish-load', async () => {
188
+ logger.log('🌐 Website loaded successfully...');
189
+ if (isDev && !extensionsLoaded) {
190
+ await loadDevExtensions();
191
+ }
192
+ mainWindow.show();
193
+ });
194
+ // Options for new windows
195
+ mainWindow.webContents.setWindowOpenHandler(({ url, frameName, features }) => {
196
+ const parsedFeatures = parseFeatures(features);
197
+ logger.log('Open window', url, features, parsedFeatures);
198
+ return {
199
+ action: 'allow',
200
+ createWindow: (options) => {
201
+ const newWindow = new BrowserWindow(options);
202
+ newWindow.setMenu(null); // Remove menu bar
203
+ return newWindow.webContents;
204
+ },
205
+ overrideBrowserWindowOptions: {
206
+ ...windowOptions,
207
+ titleBarOverlay: (process.platform === 'darwin') ? false : {
208
+ color: '#13171b',
209
+ symbolColor: '#FFFFFF',
210
+ height: 44,
211
+ },
212
+ minWidth: 800,
213
+ minHeight: 600,
214
+ width: parsedFeatures.width,
215
+ height: parsedFeatures.height,
216
+ fullscreen: parsedFeatures.fullscreen,
217
+ title: parsedFeatures.title,
218
+ }
219
+ };
220
+ });
221
+ // Get custom URL from command line arguments
222
+ const customUrl = process.argv.find(arg => arg.startsWith('--url='))?.split('=')[1];
223
+ if (customUrl) {
224
+ try {
225
+ await mainWindow.loadURL(customUrl);
226
+ logger.log('Loading custom URL:', customUrl);
227
+ }
228
+ catch (error) {
229
+ logger.error('Failed to load custom URL:', error);
230
+ await showUrlLoadError('Failed to load custom URL');
231
+ }
232
+ }
233
+ else if (isDev) {
234
+ if (process.env.DEV_HOSTED) {
235
+ try {
236
+ await mainWindow.loadURL(GENESYS_URL);
237
+ logger.log('Genesys.ai hosted');
238
+ }
239
+ catch (error) {
240
+ logger.error('Failed to load hosted URL:', error);
241
+ await showUrlLoadError('Failed to load hosted Genesys.ai');
242
+ }
243
+ }
244
+ else {
245
+ try {
246
+ await mainWindow.loadURL(GENESYS_LOCAL_URL);
247
+ logger.log('Genesys.ai localhost');
248
+ }
249
+ catch (error) {
250
+ logger.error('Failed to load local URL:', error);
251
+ await showLocalDevError();
252
+ }
253
+ }
254
+ mainWindow.webContents.openDevTools();
255
+ }
256
+ else {
257
+ try {
258
+ await mainWindow.loadURL(GENESYS_URL);
259
+ }
260
+ catch (error) {
261
+ logger.error('Failed to load production URL:', error);
262
+ await showUrlLoadError('Failed to load Genesys.ai');
263
+ }
264
+ }
265
+ }
266
+ ;
267
+ function checkForUpdates() {
268
+ // Check for GH_TOKEN environment variable
269
+ if (!process.env.GH_TOKEN) {
270
+ logger.warn('GH_TOKEN environment variable is missing - update check will be skipped');
271
+ dialog.showMessageBox({
272
+ type: 'warning',
273
+ buttons: ['OK'],
274
+ title: 'Update Check Warning',
275
+ message: 'GitHub Token Missing',
276
+ detail: 'The GH_TOKEN environment variable is not set.\n\nUpdate checks require a GitHub token to access private releases.\n\nPlease set the GH_TOKEN environment variable and restart the application to enable automatic updates.',
277
+ });
278
+ return;
279
+ }
280
+ autoUpdater.logger = log;
281
+ autoUpdater.checkForUpdatesAndNotify();
282
+ autoUpdater.on('update-available', () => {
283
+ logger.log('App update available');
284
+ mainWindow?.webContents.send('update_available');
285
+ });
286
+ autoUpdater.on('update-downloaded', info => {
287
+ const currentVersion = app.getVersion();
288
+ logger.log(`App update downloaded: v${currentVersion} -> v${info.version}`);
289
+ dialog.showMessageBox(mainWindow, {
290
+ type: 'info',
291
+ buttons: ['Restart', 'Later'],
292
+ title: 'SDK App Update Ready',
293
+ message: `Update to v${info.version} is ready to install`,
294
+ detail: `Current version: v${currentVersion}\nNew version: v${info.version}\n\nRestart now to complete the update?`,
295
+ }).then(({ response }) => {
296
+ if (response === 0)
297
+ autoUpdater.quitAndInstall();
298
+ });
299
+ });
300
+ autoUpdater.on('error', (error) => {
301
+ logger.error('Update error:', error);
302
+ });
303
+ }
304
+ let extensionsLoaded = false;
305
+ // Chrome Web Store extension IDs
306
+ const REACT_DEVELOPER_TOOLS_ID = 'fmkadmapgofadopljbjfkapdkoienihi';
307
+ const MOBX_DEVTOOLS_ID = 'pfgnfdagidkfgccljigdamigbcnndkod';
308
+ /** Get the path where Electron stores downloaded extensions */
309
+ function getExtensionsPath() {
310
+ return path.join(app.getPath('userData'), 'extensions');
311
+ }
312
+ /** Check if an extension is already downloaded */
313
+ function isExtensionDownloaded(extensionId) {
314
+ const extensionPath = path.join(getExtensionsPath(), extensionId);
315
+ return fs.existsSync(extensionPath);
316
+ }
317
+ /** Download extension using electron-devtools-installer (uses deprecated API but only for download) */
318
+ async function downloadExtension(extensionId, name) {
319
+ try {
320
+ // Dynamic import to avoid loading the module if not needed
321
+ const { installExtension } = await import('electron-devtools-installer');
322
+ await installExtension(extensionId);
323
+ logger.log(`🔧 Downloaded extension: ${name}`);
324
+ return path.join(getExtensionsPath(), extensionId);
325
+ }
326
+ catch (err) {
327
+ logger.log(`❌ Failed to download ${name}:`, err);
328
+ return null;
329
+ }
330
+ }
331
+ /** Load an extension using the new session.extensions API */
332
+ async function loadExtensionFromPath(extensionPath, name) {
333
+ try {
334
+ const extension = await session.defaultSession.extensions.loadExtension(extensionPath, { allowFileAccess: true });
335
+ logger.log(`🔧 Loaded extension: ${extension.name}`);
336
+ return true;
337
+ }
338
+ catch (err) {
339
+ logger.log(`❌ Failed to load ${name}:`, err);
340
+ return false;
341
+ }
342
+ }
343
+ /** Load development extensions only once after website loads successfully */
344
+ async function loadDevExtensions() {
345
+ // Only load extensions in dev mode or when explicitly enabled
346
+ if (!isDev && !app.commandLine.hasSwitch('devtoolextensions')) {
347
+ return;
348
+ }
349
+ if (app.commandLine.hasSwitch('devtoolextensions') && app.commandLine.getSwitchValue('devtoolextensions') === '0') {
350
+ return;
351
+ }
352
+ const extensions = [
353
+ { id: REACT_DEVELOPER_TOOLS_ID, name: 'React Developer Tools' },
354
+ { id: MOBX_DEVTOOLS_ID, name: 'MobX Developer Tools' },
355
+ ];
356
+ for (const ext of extensions) {
357
+ const extensionPath = path.join(getExtensionsPath(), ext.id);
358
+ // If extension is not downloaded, download it first (uses deprecated API internally)
359
+ if (!isExtensionDownloaded(ext.id)) {
360
+ const downloadedPath = await downloadExtension(ext.id, ext.name);
361
+ if (!downloadedPath)
362
+ continue;
363
+ }
364
+ // Load using new API
365
+ await loadExtensionFromPath(extensionPath, ext.name);
366
+ }
367
+ extensionsLoaded = true;
368
+ logger.log('🔧 Extension loading completed.');
369
+ }
@@ -0,0 +1,196 @@
1
+ import { app, Menu } from 'electron';
2
+ import { checkUpdates, openAppLog, showAbout, showInExplorer } from './actions.js';
3
+ export function buildAppMenu(mainWindow) {
4
+ const isMac = process.platform === 'darwin';
5
+ if (isMac) {
6
+ return buildMacMenu(mainWindow);
7
+ }
8
+ return buildWindowsMenu(mainWindow);
9
+ }
10
+ function buildMacMenu(mainWindow) {
11
+ const menuTemplate = [
12
+ {
13
+ label: app.name,
14
+ submenu: [
15
+ {
16
+ label: `About ${app.name}`,
17
+ click: () => showAbout(mainWindow)
18
+ },
19
+ { type: 'separator' },
20
+ {
21
+ label: 'Check for Updates',
22
+ click: checkUpdates
23
+ },
24
+ { type: 'separator' },
25
+ { role: 'services' },
26
+ { type: 'separator' },
27
+ { role: 'hide' },
28
+ { role: 'hideOthers' },
29
+ { role: 'unhide' },
30
+ { type: 'separator' },
31
+ {
32
+ label: `Quit ${app.name}`,
33
+ accelerator: 'Cmd+Q',
34
+ click: () => app.quit()
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ label: 'Project',
40
+ submenu: [
41
+ {
42
+ label: 'Show in Finder',
43
+ accelerator: 'Cmd+E',
44
+ click: showInExplorer
45
+ }
46
+ ]
47
+ },
48
+ {
49
+ label: 'Edit',
50
+ submenu: [
51
+ { label: 'Undo', role: 'undo' },
52
+ { label: 'Redo', role: 'redo' },
53
+ { type: 'separator' },
54
+ { label: 'Cut', role: 'cut' },
55
+ { label: 'Copy', role: 'copy' },
56
+ { label: 'Paste', role: 'paste' },
57
+ { label: 'Paste and Match Style', role: 'pasteAndMatchStyle' },
58
+ { label: 'Delete', role: 'delete' },
59
+ { label: 'Select All', role: 'selectAll' }
60
+ ]
61
+ },
62
+ {
63
+ label: 'View',
64
+ submenu: [
65
+ {
66
+ label: 'Reload',
67
+ accelerator: 'Cmd+R',
68
+ click: () => mainWindow?.reload()
69
+ },
70
+ {
71
+ label: 'Force Reload',
72
+ accelerator: 'Cmd+Shift+R',
73
+ click: () => mainWindow?.webContents.reloadIgnoringCache()
74
+ },
75
+ { type: 'separator' },
76
+ { label: 'Actual Size', role: 'resetZoom' },
77
+ { label: 'Zoom In', role: 'zoomIn' },
78
+ { label: 'Zoom Out', role: 'zoomOut' },
79
+ { type: 'separator' },
80
+ { label: 'Toggle Full Screen', role: 'togglefullscreen' },
81
+ {
82
+ label: 'Toggle Full Screen',
83
+ accelerator: 'F11',
84
+ visible: false,
85
+ click: () => mainWindow?.setFullScreen(!mainWindow.isFullScreen())
86
+ }
87
+ ]
88
+ },
89
+ {
90
+ label: 'Tools',
91
+ submenu: [
92
+ {
93
+ label: 'Toggle Developer Console',
94
+ accelerator: 'F12',
95
+ click: () => mainWindow?.webContents.toggleDevTools()
96
+ },
97
+ { type: 'separator' },
98
+ {
99
+ label: 'Open App Log',
100
+ click: openAppLog
101
+ }
102
+ ]
103
+ }
104
+ ];
105
+ return Menu.buildFromTemplate(menuTemplate);
106
+ }
107
+ function buildWindowsMenu(mainWindow) {
108
+ const menuTemplate = [
109
+ {
110
+ label: 'Project',
111
+ submenu: [
112
+ {
113
+ label: 'Show in Explorer',
114
+ accelerator: 'Ctrl+E',
115
+ click: showInExplorer
116
+ }
117
+ ]
118
+ },
119
+ {
120
+ label: 'Edit',
121
+ submenu: [
122
+ { label: 'Undo', role: 'undo' },
123
+ { label: 'Redo', role: 'redo' },
124
+ { type: 'separator' },
125
+ { label: 'Cut', role: 'cut' },
126
+ { label: 'Copy', role: 'copy' },
127
+ { label: 'Paste', role: 'paste' },
128
+ { label: 'Paste and Match Style', role: 'pasteAndMatchStyle' },
129
+ { label: 'Delete', role: 'delete' },
130
+ { label: 'Select All', role: 'selectAll' }
131
+ ]
132
+ },
133
+ {
134
+ label: 'View',
135
+ submenu: [
136
+ {
137
+ label: 'Reload',
138
+ accelerator: 'Ctrl+R',
139
+ click: () => mainWindow?.reload()
140
+ },
141
+ {
142
+ label: 'Force Reload',
143
+ accelerator: 'Ctrl+Shift+R',
144
+ click: () => mainWindow?.webContents.reloadIgnoringCache()
145
+ },
146
+ { type: 'separator' },
147
+ {
148
+ label: 'Toggle Developer Console',
149
+ accelerator: 'F12',
150
+ click: () => mainWindow?.webContents.toggleDevTools()
151
+ },
152
+ { type: 'separator' },
153
+ { label: 'Actual Size', role: 'resetZoom' },
154
+ { label: 'Zoom In', role: 'zoomIn' },
155
+ { label: 'Zoom Out', role: 'zoomOut' },
156
+ { type: 'separator' },
157
+ { label: 'Toggle Full Screen', role: 'togglefullscreen', accelerator: 'F11' }
158
+ ]
159
+ },
160
+ {
161
+ label: 'Tools',
162
+ submenu: [
163
+ {
164
+ label: 'Check for Updates',
165
+ click: checkUpdates
166
+ },
167
+ { type: 'separator' },
168
+ {
169
+ label: 'Open App Log',
170
+ click: openAppLog
171
+ }
172
+ ]
173
+ },
174
+ {
175
+ label: 'Help',
176
+ submenu: [
177
+ {
178
+ label: `About ${app.name}`,
179
+ click: () => showAbout(mainWindow)
180
+ }
181
+ ]
182
+ },
183
+ {
184
+ label: '',
185
+ submenu: [
186
+ {
187
+ label: `Quit ${app.name}`,
188
+ accelerator: 'Ctrl+Q',
189
+ click: () => app.quit()
190
+ }
191
+ ],
192
+ visible: false
193
+ }
194
+ ];
195
+ return Menu.buildFromTemplate(menuTemplate);
196
+ }