u2a 2.1.1 → 2.1.6

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 CHANGED
@@ -130,4 +130,4 @@ Created by [Douxx](https://douxx.tech)
130
130
 
131
131
  ## Disclaimer
132
132
 
133
- This tool is for personal use only. Always respect the terms of service of websites you convert to desktop apps.
133
+ This tool is for personal use only. Always respect the terms of service of websites you convert to desktop apps.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u2a",
3
- "version": "2.1.1",
3
+ "version": "2.1.6",
4
4
  "description": "URL to App - Turn any URL into a desktop application",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -51,7 +51,6 @@ function createWindowsShortcut(appInfo) {
51
51
  }
52
52
  }
53
53
 
54
-
55
54
  function createLinuxDesktopEntry(appInfo) {
56
55
  try {
57
56
  const { domain, url, appDir, iconPath } = appInfo;
@@ -179,9 +178,7 @@ function removeAppFromOS(domain) {
179
178
  const desktopFilePath = path.join(os.homedir(), '.local', 'share', 'applications', `u2a-${domain}.desktop`);
180
179
  if (fs.existsSync(desktopFilePath)) {
181
180
  fs.unlinkSync(desktopFilePath);
182
- logger.success(`Linux desktop entry removed: ${desktopFilePath}
183
-
184
- `);
181
+ logger.success(`Linux desktop entry removed: ${desktopFilePath}`);
185
182
  }
186
183
  }
187
184
  } catch (error) {
@@ -200,11 +197,13 @@ const APP_URL = "${url}";
200
197
  const APP_ICON_PATH = "${iconPath.replace(/\\/g, '\\\\')}";
201
198
 
202
199
  let mainWindow;
200
+ let splashWindow;
201
+ let loadErrors = [];
203
202
 
204
203
  function logAppInfo() {
205
204
  const packageJsonPath = path.join(__dirname, 'package.json');
206
205
  let packageInfo = {};
207
-
206
+
208
207
  try {
209
208
  const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8');
210
209
  packageInfo = JSON.parse(packageJsonContent);
@@ -220,25 +219,210 @@ function logAppInfo() {
220
219
  console.log(\`Started at: \${new Date().toLocaleString()}\`);
221
220
  console.log(\`App directory: \${__dirname}\`);
222
221
  console.log(\`Icon path: \${APP_ICON_PATH}\`);
223
-
222
+
224
223
  console.log('\\n PACKAGE INFO:');
225
224
  console.log(\` - Name: \${packageInfo.name || 'N/A'}\`);
226
225
  console.log(\` - Version: \${packageInfo.version || 'N/A'}\`);
227
226
  console.log(\` - Description: \${packageInfo.description || 'N/A'}\`);
228
227
  console.log(\` - Electron version: \${packageInfo.dependencies?.electron || 'N/A'}\`);
229
-
228
+
230
229
  if (packageInfo.build) {
231
230
  console.log('\\n BUILD CONFIG:');
232
231
  console.log(\` - App ID: \${packageInfo.build.appId || 'N/A'}\`);
233
232
  console.log(\` - Product Name: \${packageInfo.build.productName || 'N/A'}\`);
234
233
  }
235
-
234
+
236
235
  console.log('--------------------------------\\n');
237
236
  }
238
237
 
238
+ function updateSplashScreen(message, isError = false) {
239
+ if (splashWindow && !splashWindow.isDestroyed()) {
240
+ splashWindow.webContents.executeJavaScript(\`
241
+ try {
242
+ document.getElementById('loading-text').innerText = "\${message.replace(/"/g, '\\"')}";
243
+ } catch (e) {
244
+ console.error('Failed to update splash screen:', e);
245
+ }
246
+ \`).catch(err => console.error('Failed to update splash screen:', err));
247
+
248
+ }
249
+ }
250
+
251
+ function createSplashScreen() {
252
+ splashWindow = new BrowserWindow({
253
+ width: 550,
254
+ height: 400,
255
+ transparent: true,
256
+ frame: false,
257
+ alwaysOnTop: true,
258
+ icon: APP_ICON_PATH,
259
+ webPreferences: {
260
+ nodeIntegration: false,
261
+ contextIsolation: true
262
+ }
263
+ });
264
+
265
+ const splashHtml = \`
266
+ <!DOCTYPE html>
267
+ <html>
268
+ <head>
269
+ <meta charset="UTF-8">
270
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'; object-src 'none';">
271
+ <title>Loading...</title>
272
+ <style>
273
+ body {
274
+ margin: 0;
275
+ padding: 0;
276
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
277
+ background-color: var(--bg-primary, #0f172a);
278
+ color: var(--text-primary, #f8fafc);
279
+ height: 100vh;
280
+ display: flex;
281
+ flex-direction: column;
282
+ justify-content: center;
283
+ align-items: center;
284
+ overflow: hidden;
285
+ border-radius: 20px;
286
+ overflow: hidden;
287
+ box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.2);
288
+ }
289
+
290
+ .container {
291
+ text-align: center;
292
+ width: 90%;
293
+ max-width: 500px;
294
+ }
295
+
296
+ .domain {
297
+ font-size: 24px;
298
+ margin-bottom: 20px;
299
+ color: var(--primary, #2563eb);
300
+ }
301
+
302
+ .spinner {
303
+ width: 50px;
304
+ height: 50px;
305
+ border: 5px solid rgba(37, 99, 235, 0.2);
306
+ border-radius: 50%;
307
+ border-top-color: var(--primary, #2563eb);
308
+ animation: spin 1s ease-in-out infinite;
309
+ margin: 0 auto 20px;
310
+ }
311
+
312
+ @keyframes spin {
313
+ to { transform: rotate(360deg); }
314
+ }
315
+
316
+ .loading-text {
317
+ font-size: 16px;
318
+ color: var(--text-secondary, #cbd5e1);
319
+ margin-top: 15px;
320
+ }
321
+
322
+ .loading-text.error {
323
+ color: var(--danger, #ef4444);
324
+ font-weight: bold;
325
+ }
326
+
327
+ .progress-bar {
328
+ width: 100%;
329
+ height: 4px;
330
+ background-color: var(--bg-secondary, #1e293b);
331
+ border-radius: 2px;
332
+ overflow: hidden;
333
+ margin-top: 15px;
334
+ }
335
+
336
+ .progress {
337
+ height: 100%;
338
+ width: 0%;
339
+ background-color: var(--primary, #2563eb);
340
+ animation: progress 3s ease-in-out infinite;
341
+ }
342
+
343
+ @keyframes progress {
344
+ 0% { width: 0%; }
345
+ 50% { width: 70%; }
346
+ 100% { width: 100%; }
347
+ }
348
+
349
+ #errors-container {
350
+ display: none;
351
+ margin-top: 20px;
352
+ background-color: rgba(239, 68, 68, 0.1);
353
+ border-left: 3px solid var(--danger, #ef4444);
354
+ padding: 10px;
355
+ border-radius: 4px;
356
+ text-align: left;
357
+ max-height: 150px;
358
+ overflow-y: auto;
359
+ width: 100%;
360
+ }
361
+
362
+ #errors-list {
363
+ margin: 0;
364
+ padding-left: 20px;
365
+ color: var(--danger, #ef4444);
366
+ font-size: 14px;
367
+ }
368
+
369
+ #errors-list li {
370
+ margin-bottom: 5px;
371
+ }
372
+
373
+ .retry-button {
374
+ background-color: var(--primary, #2563eb);
375
+ color: white;
376
+ border: none;
377
+ padding: 8px 16px;
378
+ border-radius: 4px;
379
+ margin-top: 15px;
380
+ cursor: pointer;
381
+ font-weight: bold;
382
+ transition: background-color 0.2s;
383
+ display: none;
384
+ }
385
+
386
+ .retry-button:hover {
387
+ background-color: var(--primary-dark, #1d4ed8);
388
+ }
389
+ </style>
390
+ </head>
391
+ <body>
392
+ <div class="container">
393
+ <div class="domain">\${APP_DOMAIN}</div>
394
+ <div class="spinner"></div>
395
+ <div id="loading-text" class="loading-text">Loading...</div>
396
+ <div class="progress-bar">
397
+ <div class="progress"></div>
398
+ </div>
399
+ <div id="errors-container">
400
+ <h3 style="margin-top: 0; color: var(--danger, #ef4444);">Errors detected:</h3>
401
+ <ul id="errors-list"></ul>
402
+ </div>
403
+ <button id="retry-button" class="retry-button" onclick="window.location.reload()">Retry</button>
404
+ </div>
405
+ <script>
406
+ window.showRetryButton = function() {
407
+ document.getElementById('retry-button').style.display = 'inline-block';
408
+ document.querySelector('.spinner').style.animationPlayState = 'paused';
409
+ document.querySelector('.progress').style.animationPlayState = 'paused';
410
+ }
411
+ </script>
412
+ </body>
413
+ </html>
414
+ \`;
415
+
416
+ const splashPath = path.join(app.getPath('temp'), \`\${APP_DOMAIN}-splash.html\`);
417
+ fs.writeFileSync(splashPath, splashHtml);
418
+
419
+ splashWindow.loadFile(splashPath);
420
+ splashWindow.center();
421
+ }
422
+
239
423
  function createWindow() {
240
424
  logAppInfo();
241
-
425
+
242
426
  app.setAppUserModelId(APP_DOMAIN);
243
427
 
244
428
  mainWindow = new BrowserWindow({
@@ -246,12 +430,72 @@ function createWindow() {
246
430
  height: 800,
247
431
  title: APP_DOMAIN,
248
432
  icon: APP_ICON_PATH,
433
+ show: false,
249
434
  webPreferences: {
250
435
  nodeIntegration: false,
251
436
  contextIsolation: true
252
437
  }
253
438
  });
254
439
 
440
+ mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL, isMainFrame) => {
441
+ const errorMessage = \`Load error (\${errorCode}): \${errorDescription}\`;
442
+ loadErrors.push(errorMessage);
443
+
444
+ updateSplashScreen(errorMessage, true);
445
+
446
+ if (isMainFrame) {
447
+ splashWindow.webContents.executeJavaScript('window.showRetryButton()').catch(err => {
448
+ console.error('Failed to show retry button:', err);
449
+ });
450
+ }
451
+ });
452
+
453
+ mainWindow.webContents.on('certificate-error', (event, url, error, certificate, callback) => {
454
+ event.preventDefault();
455
+ const errorMessage = \`Certificate error: \${error}\`;
456
+ console.error(errorMessage);
457
+ loadErrors.push(errorMessage);
458
+
459
+ updateSplashScreen(errorMessage, true);
460
+ callback(false);
461
+ });
462
+
463
+ mainWindow.webContents.on('console-message', (event, level, message, line, sourceId) => {
464
+ if (level === 2) {
465
+ const errorMessage = \`Console: \${message} (line \${line})\`;
466
+ loadErrors.push(errorMessage);
467
+
468
+ updateSplashScreen('JavaScript error detected', true);
469
+ }
470
+ });
471
+
472
+ mainWindow.webContents.on('did-start-loading', () => {
473
+ updateSplashScreen('Connecting to ' + APP_DOMAIN + '...');
474
+ });
475
+
476
+ mainWindow.webContents.on('did-start-navigation', (event, url) => {
477
+ updateSplashScreen('Navigating to ' + new URL(url).host + '...');
478
+ });
479
+
480
+ mainWindow.webContents.on('dom-ready', () => {
481
+ updateSplashScreen('DOM ready, loading resources...');
482
+ });
483
+
484
+ mainWindow.webContents.on('did-finish-load', () => {
485
+ updateSplashScreen('Loading complete!');
486
+
487
+ setTimeout(() => {
488
+ if (splashWindow && !splashWindow.isDestroyed()) {
489
+ splashWindow.close();
490
+ }
491
+
492
+ if (!mainWindow.isVisible()) {
493
+ mainWindow.show();
494
+ mainWindow.focus();
495
+ }
496
+ }, 500);
497
+ });
498
+
255
499
  mainWindow.loadURL(APP_URL);
256
500
 
257
501
  mainWindow.webContents.on('context-menu', (e, params) => {
@@ -272,13 +516,28 @@ function createWindow() {
272
516
 
273
517
  const menu = Menu.buildFromTemplate(template);
274
518
  Menu.setApplicationMenu(menu);
519
+
520
+ setTimeout(() => {
521
+ if (splashWindow && !splashWindow.isDestroyed()) {
522
+ if (loadErrors.length === 0) {
523
+ splashWindow.close();
524
+ if (!mainWindow.isVisible()) {
525
+ mainWindow.show();
526
+ mainWindow.focus();
527
+ }
528
+ }
529
+ }
530
+ }, 15000);
275
531
  }
276
532
 
277
533
  if (process.platform === 'win32') {
278
534
  app.setAppUserModelId(app.name);
279
535
  }
280
536
 
281
- app.whenReady().then(createWindow);
537
+ app.whenReady().then(() => {
538
+ createSplashScreen();
539
+ createWindow();
540
+ });
282
541
 
283
542
  app.on('window-all-closed', () => {
284
543
  if (process.platform !== 'darwin') {
@@ -294,10 +553,9 @@ app.on('activate', () => {
294
553
  `;
295
554
  }
296
555
 
297
-
298
556
  function generatePackageJson(domain, iconPath) {
299
557
  const u2aPackagePath = path.resolve(__dirname, '../../package.json');
300
-
558
+
301
559
  let u2aVersion = '1.0.0';
302
560
  try {
303
561
  const u2aPackageContent = fs.readFileSync(u2aPackagePath, 'utf8');
@@ -306,7 +564,7 @@ function generatePackageJson(domain, iconPath) {
306
564
  } catch (error) {
307
565
  logger.error('Error while fetching u2a package.json', error)
308
566
  }
309
-
567
+
310
568
  return {
311
569
  name: `u2a-${domain}`,
312
570
  version: u2aVersion,